How to implement this Query in Hibernate? - java

I'm converting a legacy iBatis implementation to Hibernate, and for backwards compatibility purposes need to present counts of an object's collections rather than the collections themselves. The original query was:
select A.*, ( select count(*) from B where B.A_id = A.id ) as B_count from A;
and b_count would be presented in the response. I'd like to be able to do the same without lazy-loading A's collection of B's for each query result.
Any ideas or suggestions?

The best method seems to be using a Hibernate formula, mapped to the getter and setter of my BCount attribute in the class A. My code:
public class A {
// ...
private long bCount;
// ...
#Formula( "(select count(*) from B where B.A_id = id" )
public long getBCount() {
return this.bCount;
}
public void setBCount( long bCount ) {
this.bCount = bCount;
}
}
Great thing about this method is that the count is returned in the same fetch to hydrate the initial object, and does not result in 1+N queries for collection query results!

You can use a projection.
The syntax for the row count is below:
Criteria crit = session.createCriteria(B.class);
crit.setProjection(Projections.rowCount());
List results = crit.list();
Edit: After re-reading, I think this may not be what you're asking for....

Hibernate filters are used to apply additional restrictions to query results (e.g. think of them as part of "where" clause), so they won't do what you want. You have two options here:
A) You can eagerly get collection of Bs for your A:
from A a left join fetch a.Bs b
If you do so, keep in mind that for queries that would return multiple As you may get duplicates in the result list (e.g. if you have 2 As and each of them has 3 Bs you'll get 6 results back). Wrap them in a set to ensure uniqueness.
B) Assuming you have an appropriate constructor for A, you can do the following:
select new A(a.field1, a.field2, ... a.fieldN, count(*) as B_count)
from A a left join a.Bs b
group by a.field1, a.field2, ... a.fieldN

Related

JPA Specification join on fetch is not working (Hibernate, Spring Data JPA)

I am using hibernate-orm with spring-data-jpa. I have three entities A, B, C declared as follows:
#Entity
public class A {
#OneToMany(....)
private List<B> listOfB;
}
#Entity
public class B {
#ManyToOne(...)
private A a;
#OneToMany(...)
private List<C> listOfC;
}
#Entity
public class C {
#ManyToOne(...)
private B b;
}
My objective is to get A and fetch listOfB as well, with some condition on entity C without fetching it. The following JPQL is working fine.
SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
LEFT JOIN b.listOfC c
WHERE c.xyz = :xyz
When I tried using JPA Specification my Specification it looks like the following:
(rootA, query, builder) -> {
Fetch fetch = rootA.fetch(A_.listOfB, JoinType.LEFT);
ListJoin listJoin = ((ListJoin)fetch).join(B_.listOfC)
return builder.equal(listJoin.get(C_.xyz), xyz);
}
I am reusing the implicit join done by the fetch operation. This join is not working in specification. It's outputting the following JPQL.
SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
WHERE c.xyz = :xyz
Saying that, there is no c alias.
I have looked into Hibernate GitHub source code. I found out that, there is a class named QueryStructure.java responsible for generating JPQL query from the criteria object.
I found the function renderFetches which render the fetches.
#SuppressWarnings({ "unchecked" })
private void renderFetches(
StringBuilder jpaqlQuery,
RenderingContext renderingContext,
Collection<? extends Fetch> fetches) {
if ( fetches == null ) {
return;
}
for ( Fetch fetch : fetches ) {
( (FromImplementor) fetch ).prepareAlias( renderingContext );
jpaqlQuery.append( renderJoinType( fetch.getJoinType() ) )
.append( "fetch " )
.append( ( (FromImplementor) fetch ).renderTableExpression( renderingContext ) );
renderFetches( jpaqlQuery, renderingContext, fetch.getFetches() );
}
}
Similarly there is a function renderJoins responsible for all the joins.
These two are recursive functions rendering the criteria object tree.
It's clear that all the joins inside the fetches are ignored. There is no call to function renderJoins inside from renderFetches which causes the generated query to be incomplete.
Is there any in depth reason why we are not joining inside from a fetch? If yes then how could I reuse the existing implicit joins done by fetch?
This issue is re-generated using hibernate test case template.
Regenerated Hibernate test case template
Specific test case file
As i mentioned in my question, there is no renderJoins call from inside of renderFetches, adding following at the end of the renderFetches solves the issue.
if (fetch instanceof From) {
From from = (From) fetch;
renderJoins(jpaqlQuery, renderingContext, from.getJoins());
}
I have given a PR HHH-14916 in the hibernate-orm.
PR is merged will be available in the next 5.6.x release.

Efficient JPQL query for retrieving complex entities

I'm quite the rookie with JPA/JPQL, so please excuse me if this question is not crystal clear.
I am trying to find an efficient JQPL query in order to get all records of a complex object.
(ie. represented by multiple tables, with several one-to-many relationships - see simplified example below):
class ComplexObject {
private Set< SubOject1> so1 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject1>...
}
class SubObject1 {
private Set< SubOject2> so2 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject2>...
}
I am using the following JPQL query :
select distinct CO
from ComplexObject CO
left join fetch CO.so1 SO1
left join fetch SO1.so2
The query is run on a stateless session, in order to get a de facto snapshot of the current data in the DB, which is detached from the entity manager (hence the usage of left join fetch).
Unfortunately, I've encountered 2 problems :
Since the complex object contains multiple instances of so1, and each so1 instance contains multiple instances of so2, the underlying translation to SQL queries generates a specific select query per row of the product of all the table joins - a very wasteful solution. Is there a way to reduce the number of internal select queries? (This seems like the dreaded N+1 queries problem).
The JPQL query returns a ComplexObject instance per internal SQL query on the product of all the table joins - which means multiple references to the ComplexObject instances. Why does this happen on a 'select distinct' query?
The JPA framework I am using is hibernate, and the DB is HyperSQL.
The (1) issue turned out to be related to using the p6spy logging framework, which printed out all the results from a large DB table. The logging format led to an incorrect assumption that many queries where being executed.
While trying to fine tune performance, using native queries did not appear to have better performance then using JPQL queries.
Using a Native Query also resulted in Object typed results, which required post processing.
You can use View Objects to receive only the columns what you want:
StringBuilder sb = new StringBuilder();
sb.append(" SELECT new ").append(ObjectVO.class.getName()).append("(co.someInfo1, co.someInfo2, so1.someInfo )");
sb.append(" FROM ComplexObject co ");
sb.append(" JOIN co.subOject1s so1 ");
sb.append(" LEFT JOIN so1.so2 so2 ");
sb.append(" WHERE so1.id = :idSo1 AND so2 = :someThing");
Query q = em.createQuery(sb.toString());
q.setParameter("idSo1", idSo1);
q.setParameter("someThing", someThing);
List<ObjectVO> listResult = q.getResultList();
The ObjectVO class:
public class ObjectVO {
private String info1;
private Long info2;
private String info3;
public PedidoModel(String info1, Long info2, String info3){
this.info1 = info1;
this.info2 = info2;
this.info3 = info3;
}
}

can hibernate hsql automatically map the query result to a class?

I wrote a hsql:
String queryString = "select t1.a, t1.b, t2.c from table1 t1, table2 t2 where t1.id = t2.id";
and then I have a class:
class test{
String a;
String b;
String c
....//other getter and setter
}
I tried:
List = getHibernateTemplate().find(queryString);
this doesn't work, when I use test object in jsp page, it will throw out exception.
I have to manually create a test object:
List<Object[]> list = getHibernateTemplate().find(queryString);
test.seta(list.get(0)[0]);
is it possible for hibernate to automatically map the class for me in hsql ?
If you have a mapping for both table1 and table2 (see Prashant question above) you can do something like:
String queryString = "select t1 from table1 t1
inner join t1.table2 t2";
After you run the query you should have a list of t1 objects.
for(Table1 t1:listOfTable1Objects) {
t1.getA(); //for example or whatever you want to do with your object.
}
The Problem is that you do not write a HQL query. You just write a normal SQL query. In HQL, because the hibernate make the mapping from table to class, you cannot make a projection. So, if you write something like
String query = "FROM Class1 WHERE ome_condition;
without the SELECT clause, the Hibernate will be able to convert the result in the proper object.
You can see more about this here: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/queryhql.html
If you dont have a mapping, you may create a auxiliary class for this. Say ResultClass. Then you add #NamedNativeQuery and #SqlResultSetMapping annotations to the class:
#NamedNativeQuery(name="queryHehehe", query="select t1.field1 f1, t2.field2 f2 from table1 t1, table2 t2", resultSetMapping="mappingHehehe")
#SqlResultSetMapping(name="mappingHehehe", entities={
#EntityResult(entityClass=my.clazz.AuxiliaryClass.class, fields = {
#FieldResult(name="id", column="f1"),
#FieldResult(name="other_property", column="f2")
}),
})
public class AuxiliaryClass {
public Long id;
public String other_property;
}
I have never used this, but can work. Good luck.
If you need a query to return values from multiple tables and create an object of an unmapped class, then you need to either do what you're doing here, or use a ResultTransformer.
In order to do this with HibernateTemplate, you'll need to change the way you use the template, possibly using execute(HibernateCallback action), as you'll need to convert the sql query to a Criteria as described in Hibernate Reference Native SQL Chapter.
If you do want to try this, you'll probably want to use an AliasToBeanResultTransformer or AliasToBeanConstructorResultTransformer rather than writing your own transformer.

Writing a Hibernate Criteria API Query with restrictions for multiple sub-elements

I have a data model that looks like this (simplified example):
public class Address { private List<AddressLine> addressLines; }
public class AddressLine { private String value; private String type; }
I am trying to use the Criteria API to search for Addresses in the database that contain specific combinations of AddressLines. For example, to retrieve all addresses that contain the address lines {(type="CITY", value="London"), (type="COUNTRY", value="GB")}. I haven't been able to find any examples of such a query.
As far as I have been able to get is to query for an Address based on a single AddressLine.
session.createCriteria(Address.class)
.createCriteria("addressLines")
.add(Restrictions.and(Restrictions.eq("type", type), Restrictions.eq("value", value))).list()
If I add a restriction for a second address lines the SQL that hibernate generates is basically asking SELECT x WHERE x.y = 'a' AND x.y = 'b' so will never return any results.
I have found similar questions being asked before but none of them have an accepted or voted for answer.
You need to write the Criteria equivalent of
select a from Address a where
exists (select line1.id from AddressLine line1 where line1.address.id = a.id
and line1.type = 'CITY'
and line1.value = 'London')
and exists (select line2.id from AddressLine line where line2.address.id = a.id
and line2.type = 'COUNTRY'
and line2.value = 'GB')
This means writing a DetachedCriteria for each subquery, with an id projection, and using these detached criterias as argument of two Subqueries.exists() calls. The alias of the address entity in the main criteria can be used in the detached criterias to implement the line1.address.id = a.id restriction.

Assign rows of a result set with same id into a Java list using Hibernate

I'm using Hibernate to retrieve some data from a database. The result set returned by the SQL query I'm using, is something like the following:
SourceId | TargetId
1 | 10
1 | 11
1 | 12
2 | 13
2 | 14
Right now I'm having something like the following class:
#NamedNativeQuery(name = "myQuery", resultSetMapping = "myMapping",
query = "select source.id id1, target.id id2
+ "from someTable source "
+ "left join associationTable association on source.id=association.sourceId "
+ "left join someTable target on association.targetId=target.id "
+ "where source.id in(?1)")
#SqlResultSetMapping(name = "myMapping",
entities = #EntityResult(entityClass = DBCalculationElement.class, fields = {
#FieldResult(name = "targetId", column = "targetId"),
#FieldResult(name = "sourceId", column = "sourceId") }))
public class MyClass {
private String sourceId;
private String targetId;
public String getSourceId() {
return sourceId;
}
public String getTargetId() {
return targetId;
}
}
Even though everything is working fine and I'm getting the results I need, in some cases the result set is really huge (thousands of rows) so it takes several minutes to actually get the data. I'm aware that Hibernate is not the best solution performance-wise when it comes to really big result sets, but I'm trying to avoid using raw JDBC.
One workaround would be to have a list of strings as targetId. This way, and using the above resultSet as an example, I would get 2 objects, one with
sourceId = "1"
which also has a list of targetIds containing the following:
targetId = <"10", "11", "12">
and something similar for the second object with sourceId="2".
Does anyone know how can I do this using Hibernate? How can I map several rows to a list in Java?
Once, i solved this problem by writing a stored procedure.
u can write stored proc to retun comma separated values of target id for each source id and in your main query use distinct to get unique sourceId.
Hope, will work for u also.
I'm aware that Hibernate is not the best solution performance-wise when it comes to really big result sets
First of all that is not true.
...in some cases the result set is really huge (thousands of rows)...
This is not huge for a DBMS like postgres, mysql and so on...
You should consider using setMaxResults and setFirstResult for paginating your query. (it is like offset and limit).
And you should take a look at your machine and JVM configurations, if you are getting performance lacks with such a simple query (yes, there is joins. but still, simple).

Categories