I'm trying to build a complex query with Criteria.
I created the query in SQL in order to explain you exactly what I want:
SELECT ID, DATE
FROM SELLS v
WHERE DATE > (SELECT min(DATE)
FROM SELLS
WHERE CODE IN (12345, 12344)
AND ID = v.ID)
AND DATE>TO_DATE('2015-01-01', 'YYYY-MM-DD')
AND DATE<TO_DATE('2016-01-01', 'YYYY-MM-DD')
GROUP BY ID, DATE
Here is my class:
#Entity
#Table(name = "SELLS")
#Getter
public class Sells {
#Id
#Column(name = "SELL_ID")
private Integer idSell;
#Column(name = "ID")
private Integer id;
#Column(name = "DATE")
private Date date;
#Column(name = "CODE")
private String code;
}
And what I tried in Criteria (start and end are the two dates in the WHERE ('2015-01-01' and '2016-01-01'):
Criteria c = getSession().createCriteria(Sells.class);
List subquery = getSession().createCriteria(Sells.class)
.add(Restrictions.in("code", {"12345", "12344"}))
.createAlias("Sells.id", "id")
.setProjection(Projections.projectionList()
.add(Projections.min("date")))
.list();
c.add(Restrictions.ge("date", subquery));
c.add(Restrictions.ge("date", start));
c.add(Restrictions.le("date", end));
c.setProjection(Projections.projectionList()
.add(Projections.groupProperty("date"))
.add(Projections.groupProperty("id")));
I tried to use a DetachedCriteria too but failed.
DetachedCriteria query = DetachedCriteria.forClass(Sells.class, "v");
DetachedCriteria subquery = DetachedCriteria.forClass(Sells.class, "bv")
.add(Restrictions.in("bv.code", codes))
.add(Restrictions.eq("bv.id", "v.id")
.setProjection(Projections.projectionList()
.add(Projections.min("bv.date")));
query.add(Restrictions.ge("v.date", subquery))
.add(Restrictions.ge("v.date", start))
.add(Restrictions.le("v.date", end))
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("v.date"))
.add(Projections.groupProperty("v.id")));
If some one has an idea how to do that it would help me a lot !
EDIT: This looks better but still not working:
Criteria query = getSession().createCriteria(Sells.class, "v");
DetachedCriteria subquery = DetachedCriteria.forClass(Sells.class, "bv")
.add(Restrictions.in("bv.code", codes))
.add(Restrictions.eq("bv.id", "v.id")
.setProjection(Projections.projectionList()
.add(Projections.min("bv.date")));
query.add(Restrictions.ge("v.date", subquery))
.add(Restrictions.ge("v.date", start))
.add(Restrictions.le("v.date", end))
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("v.date"))
.add(Projections.groupProperty("v.id")));
org.hibernate.criterion.DetachedCriteria cannot be cast to java.util.Date
Related
I have the following entity (example):
#Entity
#Table(name = "person")
public class Person implements Serializable {
#Id
#Column(name = "person_id", columnDefinition = "UUID")
private UUID userId;
#Column(name = "name")
private String name;
#ElementCollection
#MapKeyColumn(name = "phonetype")
#Column(name = "number")
#CollectionTable(name = "person_phones", joinColumns = #JoinColumn(name = "userId"))
private Map<String, String> phoneNumbers;
}
Now, the phoneNumbers are String, String in this example. Let's assume the key is the type (like "mobile", "home", "office", "fax", "pager"...) and the value is the actual number in any text format.
I'd like to query for a person which has two phone numbers:
Select * From person where
in his phone_numbers exists phonetype = 'home' and number = '0-123-456'
and also in his phone_numbers exists phonetype = 'mobile' and number = '9-876-421'
(and possibly, dynamically others)
and name = 'John'
I already constructed a sql subquery which works:
select home.userId from
(
(SELECT userId from person_phones
where (phonetype = 'home' and number = '0-123-456'))
) as home,
(
(SELECT userId from person_phones
where (phonetype = 'mobile' and number = '9-876-421'))
) as mobile
where home.userId = mobile.userId
As said, this is just a sql subquery. I'm writing JPA 2.1 criteria query in my project. And this seems oddly complicated. Can anyone give me a hint?
Had a similar problem, solved it using multiple inner joins rather than sub queries.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Person-Test");
EntityManager em = emf.createEntityManager();
Map<String, String> phoneNumbers = new HashMap<>();
phoneNumbers.put("home","0-123-456");
phoneNumbers.put("mobile","9-876-421");
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> personRoot = query.from(Person.class);
query.select(personRoot);
phoneNumbers.forEach((k, v) -> {
MapJoin<Person, String, String> phoneNrJoinJoin = personRoot.joinMap("phoneNumbers");
phoneNrJoinJoin.on(cb.equal(phoneNrJoinJoin.key(), k), cb.equal(phoneNrJoinJoin.value(), v));
});
query.where(cb.equal(personRoot.get("name"), "John"));
List<Person> people = em.createQuery(query).getResultList();
This results in the following hibernate query (renamed aliases for clarity)
SELECT person.person_id, person.name
FROM person
INNER JOIN person_phones a
ON person.person_id = a.userid
AND (a.phonetype = ? AND a.NUMBER = ?)
INNER JOIN person_phones b
on person.person_id=b.userId
and (b.phonetype=? and b.number = ? )
WHERE
person.name = ?;
Which returns all tuples of type person where all the mentioned phone numbers match.
I am new in Hibernate, I want to translate the JPQL into Criteria Query while I am learning the CriteriaQuery in Hibernate. I am successfully able to create the JPQL but got Stopped in the criteria. Following is my scenario:
Item Class:
#Entity
#Table(name = "ITEM")
public class Item {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "ITEM_ID")
private Long id = null;
#Version
#Column(name = "OBJ_VERSION")
private int version = 0;
#Column(name = "ITEM_NAME", length = 255, nullable = false, updatable = false)
private String name;
#ManyToOne(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="itemSellerId")
private User seller;
#Transient
private User buyer;
........
User Class::
...
#OneToMany(fetch=FetchType.EAGER, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
private Set<Item> boughtItems = new HashSet<Item>();
..
My JPQL query which is running fine::
public List<Item> findBySellerOrBuyer(Long sellerId,Long buyerId) {
Query query = entityManager.createQuery("select distinct i from Item i, User u "
+ "where (i.initialPrice > 22 and i.seller.id = :sellerId) "
+ "OR "
+ "( u.id = :buyerId and i member of u.boughtItems and i.initialPrice = i.reservePrice )");
return (List<Item>) query.setParameter("sellerId", sellerId).setParameter("buyerId", buyerId).getResultList();
}
the condition is ::
A Criteria Query that gets Items that match:
a) The Seller & an initial price > 22.0
OR
b) The Buyer & a reserved price = initial price
thanks :)
Last Update
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Item> cr = builder.createQuery(Item.class);
Criterion price = Restrictions.gt("initial price", 22);
Criterion name = Restrictions.eq("seller.id","abc");
Criterion Buyer = Restrictions.eq("buyer.id","abc");
Criterion reserved_price = Restrictions.eq("reserved_price","initial_price");
// To get records matching with AND condition for price & name
LogicalExpression andExp = Restrictions.and(name, price);
LogicalExpression andExp2 = Restrictions.and(Buyer, reserved_price);
return (List<Item>) ((Criteria) cr).list();
Use below code -
Criteria cr = session.createCriteria(Item.class);
Criterion price = Restrictions.gt("initial price", 22);
Criterion name = Restrictions.eq("seller.id","abc");
Criterion Buyer = Restrictions.eq("buyer.id","abc");
Criterion reserved_price = Restrictions.eq("reserved_price","initial_price");
Disjunction objDisjunction = Restrictions.disjunction();
/* Add multiple condition separated by OR clause within brackets. */
objDisjunction.add(Restrictions.and(price , name ));
objDisjunction.add(Restrictions.and(Buyer, reserved_price));
cr.add(objDisjunction);
List results = cr.list();
I have following class in one to one relationship
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PERSON_ID")
private int personId;
#Column(name = "PERSON_NAME", nullable = false, length = 30)
private String personName;
#OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
private DrivingLicense drivingLicense;
}
#Entity
#Table(name = "DRIVING_LICENSE")
public class DrivingLicense {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "LICENSE_NUMBER")
private int licenseNumber;
#Column(name = "DATE_OF_ISSUE")
private Date dateOfIssue;
#OneToOne
#JoinColumn(name = "PERSON_ID", unique = true)
private Person person;
}
currently there are 3 rows in each table
but when I do a query on person like below
Query query = entityManager.createQuery("from Person p");
after getting resultlist its resulting in too many queries like below;
Hibernate: select person0_.PERSON_ID as PERSON_ID1_1_, person0_.PERSON_NAME as PERSON_NAME2_1_ from PERSON person0_
Hibernate: select drivinglic0_.LICENSE_NUMBER as LICENSE_NUMBER1_0_1_, drivinglic0_.DATE_OF_ISSUE as DATE_OF_ISSUE2_0_1_, drivinglic0_.PERSON_ID as PERSON_ID3_0_1_, person1_.PERSON_ID as PERSON_ID1_1_0_, person1_.PERSON_NAME as PERSON_NAME2_1_0_ from DRIVING_LICENSE drivinglic0_ left outer join PERSON person1_ on drivinglic0_.PERSON_ID=person1_.PERSON_ID where drivinglic0_.PERSON_ID=?
Hibernate: select drivinglic0_.LICENSE_NUMBER as LICENSE_NUMBER1_0_1_, drivinglic0_.DATE_OF_ISSUE as DATE_OF_ISSUE2_0_1_, drivinglic0_.PERSON_ID as PERSON_ID3_0_1_, person1_.PERSON_ID as PERSON_ID1_1_0_, person1_.PERSON_NAME as PERSON_NAME2_1_0_ from DRIVING_LICENSE drivinglic0_ left outer join PERSON person1_ on drivinglic0_.PERSON_ID=person1_.PERSON_ID where drivinglic0_.PERSON_ID=?
Hibernate: select drivinglic0_.LICENSE_NUMBER as LICENSE_NUMBER1_0_1_, drivinglic0_.DATE_OF_ISSUE as DATE_OF_ISSUE2_0_1_, drivinglic0_.PERSON_ID as PERSON_ID3_0_1_, person1_.PERSON_ID as PERSON_ID1_1_0_, person1_.PERSON_NAME as PERSON_NAME2_1_0_ from DRIVING_LICENSE drivinglic0_ left outer join PERSON person1_ on drivinglic0_.PERSON_ID=person1_.PERSON_ID where drivinglic0_.PERSON_ID=?
clearly for fetching 3 rows hibernate fired 4 queries, how to solve this problem? am I doing something wrong with relationships?
update
now if I fetch with Driving license like
Query query = entityManager.createQuery("from DrivingLicense dl");
its even more worse, 7 queries are fired.
To have a JPA vendor independent solution you could use following JPQL queries.
get all Person with a DrivingLicense
Query query = entityManager.createQuery("from Person p join fetch p.drivingLicense");
get all Person idependent if they have a DrivingLicense or not
Query query = entityManager.createQuery("from Person p left join fetch p.drivingLicense");
or using the Criteria API
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Person> criteriaQuery = criteriaBuilder.createQuery(Person.class);
Root<Person> root = criteriaQuery.from(Person.class);
root.fetch("drivingLicense", JoinType.INNER);
criteriaQuery.select(root);
List<Person> resultList = em.createQuery(criteriaQuery).getResultList();
resp.
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Person> criteriaQuery = criteriaBuilder.createQuery(Person.class);
Root<Person> root = criteriaQuery.from(Person.class);
root.fetch("drivingLicense", JoinType.LEFT);
criteriaQuery.select(root);
List<Person> resultList = em.createQuery(criteriaQuery).getResultList();
This seems due to your query, i.e
Query query = entityManager.createQuery("from Person p");
Instead use:
session.createCriteria();
This will fire only 1 query to fetch Person:
Hibernate: select this_.id as id1_1_1_, this_.name as name2_1_1_,
drivinglic2_.id as id1_0_0_, drivinglic2_.DL_no as DL_no2_0_0_,
drivinglic2_.PERSON_ID as PERSON_I3_0_0_ from PERSON this_ left outer
join DRIVING_LICENSE drivinglic2_ on this_.id=drivinglic2_.PERSON_ID
order by this_.id asc
I have the following two different HQL statements.
My Data Structure looks like this:
User
#Entity (name = "User")
public class User
{
#Id
#GeneratedValue
#Column (name = "id")
private int id;
#Column (name = "user_name")
private String username;
#Column (name = "password")
private String password;
#Column (name = "enabled")
private boolean enabled;
#ManyToMany (targetEntity = Role.class, cascade =
{
CascadeType.ALL
})
#JoinTable (name = "user_role", joinColumns =
{
#JoinColumn (name = "user_id")
}, inverseJoinColumns =
{
#JoinColumn (name = "role_id")
})
private Set<Role> roles;
/* getters and setters)
}
To cut it short the only difference between the two queries is that one is ASC the other is DESC
#NamedQuery (name = "user.getUsersOrderByRoleAsc",
query = "FROM User as u left outer join u.roles roles WHERE u.username like :username ORDER BY roles.name ASC"),
#NamedQuery (name = "user.getUsersOrderByRoleDesc",
query = "FROM User as u left outer join u.roles roles WHERE u.username like :username ORDER BY roles.name DESC"),
The query for ASC returns: A list of Users -> As I would expect.
The query of DESC returns: An List of Object[], and in each object the [0] is the User, while the [1] is just another null object.
That does not make any sense to me. How can simply changing ASC to DESC change the structure of the result set ?
I am using Hibernate 4.3.6.Final.
The fastest way to determin, what went wrong is to set the show_sql flag to true in you hibernate configuration file. This will log every rendered query.
See Hibernate show real SQL
Probably there is some Hibernate bug you bumped into, but because you are join fetching a one to many children collections, it's safer to use distinct as well:
#NamedQuery (name = "user.getUsersOrderByRoleAsc",
query = "select distinct u FROM User as u left outer join u.roles roles WHERE u.username like :username ORDER BY roles.name ASC"),
#NamedQuery (name = "user.getUsersOrderByRoleDesc",
query = "select distinct u FROM User as u left outer join u.roles roles WHERE u.username like :username ORDER BY roles.name DESC")
I've the following schema:
TABLE EMPLOYEE TABLE COUNTRY
--------------- ---------------
id |------> id
name | country_name
country_id ------| country_code
If I want to retrieve all the employees belonging to a country I use a criteria like this:
Criteria crit = getSession().createCriteria(Employee.class);
crit.add(Restrictions.eq("country.id", countryId));
List<Employee> employees = crit.list();
My employee entity refers to country in this way:
#Entity
public class Employee {
private static final long serialVersionUID = 1L;
#Id
private Integer id;
#ManyToOne
#JoinColumn(name="country_id")
private Country country;
// Other stuff here
}
Everything here works fine!
The problem is if I don't know the countryId, but I have the country_code instead(i.g. "UK"). How should I change the criteria above so that I can join the tables and add the restriction on the country code?
You can do in this way, simple add if else
Criteria crit = getSession().createCriteria(Employee.class);
if(StringUtils.isNotBlank(countryId))
crit.add(Restrictions.eq("country.id", countryId));
else
crit.add(Restrictions.eq("country.code", countryCode));
List<Employee> employees = crit.list();
Criteria criteria = getSession().createCriteria(Employee.class,"e");
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
criteria.createAlias("e.country", "c");
Criterion codeId= Restrictions.eq("c.id", yourCountryId);
Criterion codeCriterion= Restrictions.eq("c.countryCode", yourCountryCode);
criteria.add(Restrictions.or(codeId, codeCriterion));
List<Employee> employees = criteria.list();
OR
Criteria criteria = getSession().createCriteria(Employee.class);
Criterion codeId = Restrictions.eq("country.id", yourCountryId);
Criterion codeCriterion = Restrictions.eq("country.countryCode", yourCountryCode);
criteria.add(Restrictions.or(codeId, codeCriterion));
List<Employee> employees = crit.list()