Hibernate Criteria Left Joining Two Tables - java

I have two entities, say Business and Area.
Relevant properties:
Business - area, area2, code
Area - areaId, areaName
area and area2 of Business map to the id in Area
I'm trying to write a Hibernate criteria that returns all the areas with businesses only.
SQL looks like:
FROM area a LEFT OUTER JOIN business b on a.areaId = b.area or a.areaId = b.area2
WHERE b.code != null
GROUP BY a.areaName
This is what I have:
DetachedCriteria criteria = DetachedCriteria.forClass(Business.class)
.setProjection(Property.forName("area"))
.setProjection(Property.forName("area2"))
.add(Restrictions.ne("code", null));
Criteria criteriaArea = fullTextSession.createCriteria(Area.class)
.createAlias("areaId", "areaId", CriteriaSpecification.LEFT_JOIN)
.add(Property.forName("areaId").in(criteria));
But this doesn't work, I get a "not an association: areaId" query exception.
Any ideas why this is happening? Thanks.

createAlias() joins another entity using provided property. Hibernate calculates what table to join using mapping of provided property. But areaId isn't mapped as a #ManyToOne or #ManyToMany reference to Business entity. So Hibernate doesn't understand to what table you want to join using Area.areaId.
Your criteria will be translated to SQL like:
select a.* from Area a
left join <here should be table referenced by areaId> b on a.areaId = b.id
where a.areaId in (
select area, area2 from Business where code <> null
)
You may rewrite query without unused join:
DetachedCriteria criteria1 = DetachedCriteria.forClass(Business.class)
.setProjection(Property.forName("area"));
DetachedCriteria criteria2 = DetachedCriteria.forClass(Business.class)
.setProjection(Property.forName("area2"));
Criteria criteriaArea = fullTextSession.createCriteria(Area.class)
.add(Restrictions.or(
Property.forName("areaId").in(criteria1),
Property.forName("areaId").in(criteria2)
);

Related

How to get batching using the old hibernate criteria?

I'm still using the old org.hibernate.Criteria and get more and more confused about fetch modes. In various queries, I need all of the following variants, so I can't control it via annotations. I'm just switching everything to #ManyToOne(fetch=FetchType.LAZY), as otherwise, there's no change to change anything in the query.
What I could find so far either concerns HQL or JPA2 or offers just two choices, but I need it for the old criteria and for (at least) the following three cases:
Do a JOIN, and fetch from both tables. This is OK unless the data is too redundant (e.g., the master data is big or repeated many times in the result). In SQL, I'd write
SELECT * FROM item JOIN order on item.order_id = order.id
WHERE ...;
Do a JOIN, fetch from the first table, and the separation from the other. This is usually the more efficient variant of the previous query. In SQL, I'd write
SELECT item.* FROM item JOIN order on item.order_id = order.id
WHERE ...;
SELECT order.* FROM order WHERE ...;
Do a JOIN, but do not fetch the joined table. This is useful e.g., for sorting based on data the other table. In SQL, I'd write
SELECT item.* FROM item JOIN order on item.order_id = order.id
WHERE ...
ORDER BY order.name, item.name;
It looks like without explicitly specifying fetch=FetchType.LAZY, everything gets fetched eagerly as in the first case, which is sometimes too bad. I guess, using Criteria#setFetchMode, I can get the third case. I haven't tried it out yet, as I'm still missing the second case. I know that it's somehow possible, as there's the #BatchSize annotation.
Am I right with the above?
Is there a way how to get the second case with the old criteria?
Update
It looks like using createAlias() leads to fetching everything eagerly. There are some overloads allowing to specify the JoinType, but I'd need to specify the fetch type. Now, I'm confused even more.
Yes you can satisfy all three cases using FetchType.LAZY, BatchSize, the different fetch modes, and projections (note I just made up a 'where' clause with Restrictions.like("name", "%s%") to ensure that I retrieved many rows):
Do a JOIN, and fetch from both tables.
Because the order of an item is FetchType.LAZY, the default fetch mode will be 'SELECT' so it just needs to be set as 'JOIN' to fetch the related entity data from a join rather than separate query:
Session session = entityManager.unwrap(org.hibernate.Session.class);
Criteria cr = session.createCriteria(Item.class);
cr.add(Restrictions.like("name", "%s%"));
cr.setFetchMode("order", FetchMode.JOIN);
List results = cr.list();
results.forEach(r -> System.out.println(((Item)r).getOrder().getName()));
The resulting single SQL query:
select
this_.id as id1_0_1_,
this_.name as name2_0_1_,
this_.order_id as order_id3_0_1_,
order2_.id as id1_1_0_,
order2_.name as name2_1_0_
from
item_table this_
left outer join
order_table order2_
on this_.order_id=order2_.id
where
this_.name like ?
Do a JOIN, fetch from the first table and the separately from the other.
Leave the fetch mode as the default 'SELECT', create an alias for the order to use it's columns in sorting, and use a projection to select the desired subset of columns including the foreign key:
Session session = entityManager.unwrap(org.hibernate.Session.class);
Criteria cr = session.createCriteria(Item.class);
cr.add(Restrictions.like("name", "%s%"));
cr.createAlias("order", "o");
cr.addOrder(org.hibernate.criterion.Order.asc("o.id"));
cr.setProjection(Projections.projectionList()
.add(Projections.property("id"), "id")
.add(Projections.property("name"), "name")
.add(Projections.property("order"), "order"))
.setResultTransformer(org.hibernate.transform.Transformers.aliasToBean(Item.class));
List results = cr.list();
results.forEach(r -> System.out.println(((Item)r).getOrder().getName()));
The resulting first SQL query:
select
this_.id as y0_,
this_.name as y1_,
this_.order_id as y2_
from
item_table this_
inner join
order_table o1_
on this_.order_id=o1_.id
where
this_.name like ?
order by
o1_.id asc
and subsequent batches (note I used #BatchSize(value=5) on the Order class):
select
order0_.id as id1_1_0_,
order0_.name as name2_1_0_
from
order_table order0_
where
order0_.id in (
?, ?, ?, ?, ?
)
Do a JOIN, but do not fetch the joined table.
Same as the previous case, but don't do anything to prompt the loading of the lazy-loaded orders:
Session session = entityManager.unwrap(org.hibernate.Session.class);
Criteria cr = session.createCriteria(Item.class);
cr.add(Restrictions.like("name", "%s%"));
cr.createAlias("order", "o");
cr.addOrder(Order.asc("o.id"));
cr.setProjection(Projections.projectionList()
.add(Projections.property("id"), "id")
.add(Projections.property("name"), "name")
.add(Projections.property("order"), "order"))
.setResultTransformer(org.hibernate.transform.Transformers.aliasToBean(Item.class));
List results = cr.list();
results.forEach(r -> System.out.println(((Item)r).getName()));
The resulting single SQL query:
select
this_.id as y0_,
this_.name as y1_,
this_.order_id as y2_
from
item_table this_
inner join
order_table o1_
on this_.order_id=o1_.id
where
this_.name like ?
order by
o1_.id asc
My entities for all cases remained the same:
#Entity
#Table(name = "item_table")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private Order order;
// getters and setters omitted
}
#Entity
#Table(name = "order_table")
#BatchSize(size = 5)
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// getters and setters omitted
}

Hibernate doesn't return proper objects in inner join

I'm working on Spring MVC application that uses Hibernate. One of my requests in SQL looks like this:
SELECT work_order.* FROM work_order
inner join user
on
work_order.user_id = user.id
and
user.user_name = 'Jenna'
and I do get a result as a row of workorders. When I'm trying to do the same with Hibernate I get five objects(which is correct) but can't convert into WorkoOrder objects.
Here's my Hibernate request:
List<WorkOrder> workorders = (List<WorkOrder>)currentSession.createQuery(
"from WorkOrder w inner join w.user as u where user_name=:tempName")
.setParameter("tempName", tempName).getResultList();
Where tempName is a parameter. I do get objects, but can't cast them to Workorder, probably because Hibernate returns Workorders and Users combined. How to fix this so only Workorders will be returned?
Update: User is mapped in WorkOrder
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="user_id", nullable = true)
private User user;
Your HQL does not have a select clause. If you want only the WorkOrder entities from the join you form, then you'll need to tell Hibernate so:
currentSession.createQuery(
"select w from WorkOrder w inner join w.user as u where u.user_name=:tempName")

How to use Oracle's 'connect by' clause in a JPA criteria query?

I want to show parent child relation in a query using jpa criteria.
But stuck with how to set connect by expression
ReadAllQuery raQuery = JpaHelper.getReadAllQuery(transquery);
raQuery.setHierarchicalQueryClause(startswith, connectBy, order by);
where transquery is made from criteriaQuery with multiple selection and multiple joins
I want to show parent and their child transactions as follow,but using in criteria query
select t0.state,t4.name,t7.money from transaction t0
LEFT OUTER JOIN transactionAgents t4 ON (t4.TRANSID = t0.ID)
LEFT OUTER JOIN AgentReference t7 on t7.id=t4.agentid
where t0.transdate>'10-july-2014' and t0.circle=10
connect by prior t0.id=t0.parent ORDER BY t0.LAST_MODIFIED ASC, t0.ID DESC
ReadAllQuery.setHierarchicalQueryClause() is an Eclispelink-specific feature and the javadoc of this method provides a good description of all method parameters to be passed:
Expression startWith = builder.get("id").equal(new Integer(100));
//can be any expression which identifies a set of employees
Expression connectBy = builder.get("managedEmployees");
//indicated the relationship that the hierarchy is based on, must be self-referential
Vector orderBy = new Vector();
orderBy.addElement(builder.get("startDate"));
readAllQuery.setHierarchicalQueryClause(startWith, connectBy, orderBy);
//This query would generate SQL like this:
//SELECT * FROM EMPLOYEE START WITH ID=100 CONNECT BY PRIOR ID = MANAGER_ID ORDER SIBLINGS BY START_DATE

How to create a JPA query with LEFT OUTER JOIN

I am starting to learn JPA, and have implemented an example with JPA query, based on the following native SQL that I tested in SQL Server:
SELECT f.StudentID, f.Name, f.Age, f.Class1, f.Class2
FROM Student f
LEFT OUTER JOIN ClassTbl s ON s.ClassID = f.Class1 OR s.ClassID = f.Class2
WHERE s.ClassName = 'abc'
From the above SQL I have constructed the following JPQL query:
SELECT f FROM Student f LEFT JOIN f.Class1 s;
As you can see, I still lack the condition OR s.ClassID = f.Class2 from my original query. My question is, how can I put it into my JPQL?
Write this;
SELECT f from Student f LEFT JOIN f.classTbls s WHERE s.ClassName = 'abc'
Because your Student entity has One To Many relationship with ClassTbl entity.
If you have entities A and B without any relation between them and there is strictly 0 or 1 B for each A, you could do:
select a, (select b from B b where b.joinProperty = a.joinProperty) from A a
This would give you an Object[]{a,b} for a single result or List<Object[]{a,b}> for multiple results.
Normally the ON clause comes from the mapping's join columns, but the JPA 2.1 draft allows for additional conditions in a new ON clause.
See,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#ON
Please see :
public interface YourDBRepository extends JpaRepository<Employee, Long> {
#Query("select new com.mypackage.myDTO(dep.empCode, dep.empName, em.EmployeeCode, em.EmployeeName) \n" +
"from Department dep\n" +
"left join Employee em\n" +
"on dep.DepartmentCode = em.DepartmentCode") // this is JPQL so use classnames
List<myDTO> getDeptEmployeeList();
}
You can also use CrudRepository and include #JoinColumn with FK table class in PK table class and have List return list and then do find operation to achieve the same.
In Department entity class:
#OneToMany
#Fetch(FetchMode.JOIN)
#JoinColumn(name="DEPT_CODE")
private List<Employee> employees;
CriteriaBuilder is yet another option.

Hibernate left join fetch - get only an ID list of the first table

I have the following HQL query which works fine, however it returns a list of full FooD objects. I only need the ID of the FooD objects as I need to have faster query. Please not that in Hibernate mappings, FooD has a many-to-one relationship with FooB.
hqlQuery = "from FooD d left join fetch d.bill where d.ts < :ts"
I have then tried to get only the ID using the same kind of HQL query:
hqlQuery = "SELECT d.id from FooD d left join fetch d.bill where d.ts < :ts"
I got a "query specified join fetching, but the owner of the fetched association was not present in the select list".
I have then converted the query to regular Oracle SQL to get only FooD.ID:
sqlQuery = "SELECT d.id from FooD d LEFT OUTER JOIN FooB b on d.foodId=b.id where d.ts < :ts"
I have then mapped FooD and FooB objects like this:
sqlQuery.addEntity(FooD.class);
sqlQuery.addEntity(FooB.class);
and then get the resulting list by calling:
hSession.createSQLQuery(sql).setTimestamp("ts", ts).list();
But got the following error: "unexpected token: on near line 1".
Does someone know how to do get only the ID of FooD when doing a left outer join on FooB using Hibernate?
Update:
I didn't test it, but this should do the trick
SELECT d.id from FooD d inner join d.bill where d.ts < :ts
when you add LEFT you make it an outer join implicitly, and there is no need to initialize bill if all you need is to join by keys
Hibernate requires the object to be in the select clause to do any eager join fetches on it
But since you have no select or where clauses on d.bill, why do you need to fetch it anyway?
If all you need is the id, why not do this, there is no reason for the redundant join:
hqlQuery = "SELECT d.id from FooD d where d.ts < :ts"

Categories