Spring data: order by result of condition for each row - spring-data

In plain SQL is possible to calculate runtime variables, like IF/SUM for each row, and possible to use it for ordering.
My question about possibility of doing things like that in QueryDSL/Criteria API
Is it possible or not?
I have entity called OrderSummary, with following fields:
#Entity
#Table(name = "orders")
public class OrderSummary implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue
private Long id;
#Column(name="title")
private String title;
#Column(name = "fixing_due_date")
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime fixingDueDate;
...
}
And I have generated Q-class.
So i try to apply for ordering following specifier:
private OrderSpecifier<Boolean> order() {
QOrderSummary orderSummary = QOrderSummary.orderSummary;
DateTimePath<DateTime> fixingDueDate = orderSummary.fixingDueDate;
BooleanExpression be = fixingDueDate.after(DateTime.now());
OrderSpecifier<Boolean> ose = be.desc();
return ose;
}
But i got exception "com.mysema.query.types.PredicateOperation cannot be cast to com.mysema.query.types.Path"
I asked a similar question (about CriteriaAPI), but unfortunatelly nobody understand the real meaning of:
JpaRepository: Spring Sort for runtime query variabels
The question about order by result of condition for each row.
Thanks!

So the solution was found, but in my preferred way with jpa specifications:
JpaRepository: Spring Sort for runtime query variabels

Related

Repository findIn confusion

My database has an Exchanges class which contains a list of CurrencyPairs.
Is it possible to use to use a Repository method to directly obtain a CurrencyPair which matches on name within a given Exchange? I'm thinking of something like
CurrencyPairDbo findByExchangeNameAndCurrencyPairIn(...)
but I can't see quite how to tie it all together. Or do I need to write a custom query for this? And does this need to be in the ExchangeRepository or the CurrencyPairRespository?
#Entity()
#Table(name = "Exchanges")
public class ExchangeDbo {
#Id #GeneratedValue
#Getter private Long id;
#Getter private String exchangeName;
#OneToMany(mappedBy = "exchange",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.EAGER)
#BatchSize(size=100)
#Getter private List<CurrencyPairDbo> listCurrencyPair = new ArrayList<>();
...
}
#Entity()
public class CurrencyPairDbo {
#Id #GeneratedValue
#Getter private Long id;
#Column(unique=true)
private String currencyPair;
#ManyToOne(fetch=FetchType.EAGER)
#Getter private ExchangeDbo exchange;
...
}
Edit:
I'm thinking it's not Find...In that I want at all. I think that something like:
List<CurrencyPairDbo> x = exchangeRepository.findByExchangeNameLowercaseAndListCurrencyPairCurrencyPair(exchangeName.toLowerCase(), currencyPair);
might work, except that in returns an Exchange object and a:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [biz.ianw.coindatabase.database.ExchangeDbo] to type [biz.ianw.coindatabase.database.CurrencyPairDbo]
This, in the currency pair repository, seems to do the job.
I added a lower case field for matching purposes and an index for efficiency.
CurrencyPairDbo findByExchangeExchangeNameLowercaseAndCurrencyPairNameLowercase( String exchangeName, String currencyPair );

JPA Error joining table and view

I need to join a table and a view in a JPA query. The query won't compile because the view columns can't be identified.
Any suggestions are greatly appreciated.
Updated with parent entity and consistent naming
The query is:
select count(m.id)
from MultiSpeedMotor m,
MultiSpeedQuery q1
where m.id = q1.motorId
and q1.power = 10
The errors are:
The state field path 'q1.motorId' cannot be resolved to a valid type.
The state field path 'q1.power' cannot be resolved to a valid type.
I am working with a legacy database that has a denormalized table similar to this
Long motorId
Long id
Double hi_power
Double lo_power
I have used a view with a union query to normalize this table into
Long motorId
Long id
Long hi
Double power
To model the view of union query in JPA, I have used an #IdClass
public class MultiSpeedQueryId implements Serializable {
private static final long serialVersionUID = -7996931190943239257L;
private Long motorId;
private Long id;
private Long hi;
...
}
#Entity
#Table(name = "multi_speed_query")
#IdClass(MultiSpeedQueryId.class)
public class MultiSpeedQuery implements IMultiSpeedQuery {
#Id
#Column(name = "motor_id")
private Long motorId;
#Id
private Long id;
#Id
private Long hi;
private Double power;
...
}
The parent Entity is mapped as:
#Entity
#Table(name = "multi_speed_motor")
public class MultiSpeedMotor implements Serializable, IMultiSpeedMotor {
private static final long serialVersionUID = 3019928176257499187L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
The query is correct as written.
You CAN join Entities with no pre-defined relationship by using the syntax.
where a.id = b.joinField
The issue was much simpler. I missed part of the JPA error log that was telling the real problem.
The abstract schema type 'MultiSpeedQuery' is unknown.
Once I added the Entity to the persistence.xml, the query, as originally written, worked perfectly.

Issues with bidirectional OneToMany mapping and NamedQuery

I have two entities connected bidirectional and I want to query the Location and its votes only for a specific date.
Location:
#Entity
#Table(name = "TAB_LOCATION")
#NamedQueries({
#NamedQuery(name = "Location.getVotedLocations", query = "SELECT l FROM Location l JOIN l.votes v WHERE v.location = l AND DATE(v.createdAt) = DATE(:date) ORDER BY l.name")
})
public class Location extends AbstractEntity {
#Basic
#Size(min = 5, max = 50)
private String name;
#Basic
#Size(min = 0, max = 50)
private String address;
#OneToMany(mappedBy = "location")
private Set<Vote> votes;
#Basic
private String description;
Vote:
#Entity
#Table(name = "TAB_VOTE")
public class Vote extends AbstractEntity {
#Basic
#ManyToOne
#NotNull
private User user;
#Basic
#ManyToOne
#NotNull
private Location location;
I was trying to use the named query but it seems that the Location always contains all votes regardless the condition.
Isn't it possible to map the queried values back to the object?
entityManager.createNamedQuery("Location.getVotedLocations").
setParameter("date", date).getResultList();
What is wrong?
If it isn't possible with NamedQueries, I also can use the Criteria API.
Here's a simple MySql statement that will return locationIDs not Location entity which are voted on a particular date.
Select DISTINCT LocationID FROM VOTE WHERE DATE == dateCreated;
And you can then get the Location entity by LocationID.
When you get an entity as a result from some query, you get the whole entity. It is not possible in JPA to get just a subset of all data, trimmed by where condition.
Well, if you use Hibernate, take a look at Hibernate Filters, with them you could get the result you want.
Note about your query, you have JOIN l.votes so you don't need to join it again with WHERE v.location = l.

How to make a CriteriaBuilder join with a custom "on" condition?

I want make a query where I join 2 tables, using the CriteriaBuilder. In MySQL the query I'm trying to make would look like this:
SELECT * FROM order
LEFT JOIN item
ON order.id = item.order_id
AND item.type_id = 1
I want to get all orders and if they have an item of type #1, I want to join with this item. However, if no item of type #1 is found, I still want to get the order. I can't figure out how to make this with the CriteriaBuilder. All I know how to make is:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
Root<Order> order = cq.from(Order.class);
Join<Order, Item> item = order.join(Order_.itemList, JoinType.LEFT);
Join<Item, Type> type = order.join(Item_.type, JoinType.LEFT);
cq.select(order);
cq.where(cb.equal(type.get(Type_.id), 1));
This query is broke, since it results in something like this in MySQL:
SELECT * FROM order
LEFT JOIN item
ON order.id = item.order_id
WHERE item.type_id = 1
The result will only contain orders with items of type #1. Orders without are excluded. How can I use the CriteriaBuilder to create a query like in the first example?
It is possible starting from the version 2.1 of JPA using the on method Join<Z, X> on(Predicate... restrictions);
Here is how:
Root<Order> order = cq.from(Order.class);
Join<Order, Item> item = order.join(Order_.itemList, JoinType.LEFT);
item.on(cb.equal(item.get(Item_.type), 1));
I think this is the same problem as posed in this question. It looks like it is not possible in CriteriaBuilder. It is possible in Hibernate Criteria API, but that probably won't help you.
JPA Criteria API: Multiple condition on LEFT JOIN
I know this question was made a long time a go, but recently a had the same problem and i found this solution from an Oracle forum, i copied and pasted just in case the link is not longer available.
MiguelChillitupaArmijos 29-abr-2011 1:41 (en respuesta a 840578) Think
you should use something like:
em.createQuery("SELECT DISTINCT e.Id" +
" from Email e " +
" left join e.idEmailIn e2 *with* e2.responseType = 'response'" +
" where e.type = 'in' and e.responseMandatory = true").getSingleResult();
An this is the link.
JPA Criteria : LEFT JOIN with an AND condition
There is a workaround if you are using Hibernate 3.6 with JPA 2.0
It is not the better solution, however it works perfect for me.
I´ve duplicate the entity with the #Where hibernate annotation.It means that everytime you use the join with this entity, hibernate will add the extra condition on the join statement at generated SQL.
For instance, initially we have the follow example:
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long id;
#Id
#Column(name = "PERSON_NAME")
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
}
#Entity
#Table(name = "ADDRESS")
public class Address {
#Id
#Column(name = "ADDRESS_ID")
private Long id;
#Id
#Column(name = "ADDRESS_STREET")
private String street;
#ManyToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
In order to add extra conditions on criteria Join, we need duplicate the Address #Entity mapping , adding the #Where annotation #Where(clause = " ADDRESS_TYPE_ID = 2").
#Entity
#Table(name = "ADDRESS")
#Where(clause = " ADDRESS_TYPE_ID = 2")
public class ShippingAddress {
#Id
#Column(name = "ADDRESS_ID")
private Long id;
#Id
#Column(name = "ADDRESS_STREET")
private String street;
#OneToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
Also, we need to add the duplicate mapping association for the new entity.
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long id;
#Id
#Column(name = "PERSON_NAME")
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
#OneToOne(mappedBy = "person")
private ShippingAddress shippingAddress;
}
Finally, you can use a join with this specific Entity in your criteria :
PersonRoot.join(Person_.shippingAddress, JoinType.LEFT);
The Hibernate Snippet SQL should seems like this :
left outer join
address shippingadd13_
on person11_.person_id=shippingadd13_.person_id
and (
shippingadd13_.ADDRESS_TYPE_ID = 2
)
ON clause is supported in Hibernate 4.3 version, anyone is aware if there is a parameter indexing issue between the parameter index of the additional custom conditions with the index of the existing mapping filters when doing an outer join with ON clause?
Using the Person entity class below as an example, say I am adding this filter to limit the address types and the filter is enabled to populate the IN clause. The parameter index for the IN clause will cause the issue [2] when I add additional conditions (such as using 'street' column) part of the ON clause. Is is a known issue?
[1] #Filter(name = "addressTypes", condition = "ADDRESS_TYPE in (:supportedTypes)")
[2]
Caused by: ERROR 22018: Invalid character string format for type BIGINT.
private Set addresses;

JPA Query Many To One nullable relationship

I have the following entities and would like to seek help on how to query for selected attributes from both side of the relationship. Here is my model. Assume all tables are properly created in the db. JPA provider I am using is Hibernate.
#Entity
public class Book{
#Id
private long id;
#Column(nullable = false)
private String ISBNCode;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = false)
private Person<Author> author;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
private Person<Borrower> borrower;
}
#Inheritance
#DiscriminatorColumn(name = "personType")
public abstract class Person<T>{
#Id
private long id;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Info information;
}
#Entity
#DiscriminatorValue(PersonType.Author)
public class Author extends Person<Author> {
private long copiesSold;
}
#Entity
#DiscriminatorValue(PersonType.Borrower)
public class Borrower extends Person<Borrower> {
.....
}
#Entity
public class Info {
#Id
private long id;
#Column(nullable=false)
private String firstName;
#Column(nullable=false)
private String lastName;
......;
}
As you can see, the book table has a many to one relation to Person that is not nullable and Person that is nullable.
I have a requirement to show, the following in a tabular format -
ISBNCode - First Name - Last Name - Person Type
How can I write a JPA query that will allow me to select only attributes that I would want. I would want to get the attributes ISBN Code from Book, and then first and last names from the Info object that is related to Person Object that in turn is related to the Book object. I would not want to get all information from Info object, interested only selected information e.g first and last name in this case.
Please note that the relation between the Borrower and Book is marked with optional=true, meaning there may be a book that may not have been yet borrowed by someone (obviously it has an author).
Example to search for books by the author "Marc":
Criteria JPA Standard
CriteriaQuery<Book> criteria = builder.createQuery( Book.class );
Root<Book> personRoot = criteria.from( Book.class );
Predicate predicate = builder.conjunction();
List<Expression<Boolean>> expressions = predicate.getExpressions();
Path<Object> firtsName = personRoot.get("author").get("information").get("firstName");
expressions.add(builder.equal(firtsName, "Marc"));
criteria.where( predicate );
criteria.select(personRoot);
List<Book> books = em.createQuery( criteria ).getResultList();
Criteria JPA Hibernate
List<Book> books = (List<Book>)sess.createCriteria(Book.class).add( Restrictions.eq("author.information.firstName", "Marc") ).list();
We recommend using hibernate criterias for convenience and possibilities.
Regards,