When does Hibernate create aliases for Subcriterias? - java

Suppose I create a Criteria using Hibernate:
Session session = (Session) entityManager.getDelegate();
Criteria rootCriteria = session.createCriteria(entityClass);
If I do not set an alias explicitly Hibernate will use "this" as an alias (I checked out and it is hardcoded into the CriteriaImpl class.
If I create a subcriteria for this one:
Criteria subCriteria = rootCriteria.createCriteria(associationPath, CriteriaSpecification.LEFT_JOIN);
Hibernate creates an instance of Subcriteria which is an inner class of CriteriaImpl and it does not assign an alias to it in the process.
If I run the query however and check my SQL log I can see that Hibernate created an alias for it but only when I ran the query. Since I want to add Restrictions to my query using this Subcriteria it would be nice if I had an alias generated for me. Is there a way to make Hibernate generate that alias or I'll have to pass them to the factory methods?

normally createCriteria is used when chaining restrictions without the need to use aliases
Criteria crit = session.createCriteria(Order.class)
.createCriteria("Items")
.add(Restrictions.gt("count", 1))
.add(Restrictions.gt("price", 10))
.list();
will generate
SELECT * FROM orders o join items i ON ... WHERE i.count > 1 and i.price > 10
with alias it looks like
Criteria crit = session.createCriteria(Order.class)
.createAlias("Items", "item")
.add(Restrictions.gt("item.count", 1))
.add(Restrictions.gt("item.price", 10))
.list();

Related

How to load multiple Hibernate entities in order of the identifiers or Primary Keys

How do we load multiple entities using Hibernate in the order of the list of Pks that is provided to the Hibernate query?
In the code below, the order of the list output is in ascending order rather than the order in which the Pks is supplied in the argument
Criteria criteria = s.createCriteria(entityClass).add(Restrictions.in(idPropertyName, pks));
List list = criteria.list();
You get them, then sort them using a comparator that compares the index of each entity in the list.
For example:
Map<Long, Integer> indexById = new HashMap<>();
for (int i = 0; i < pks.size(); i++) {
indexById.put(pks.get(i), i);
}
List<MyEntity> entities = seachByIds(pks);
entities.sort(Comparator.comparing(entity -> indexById.get(entity.getId())));
As I explained in this article, there are several ways you can achieve this goal.
In your example, you were using the legacy Hibernate Criteria, but since it's been deprecated since Hibernate 4 and will probably be removed in Hibernate 6.
Therefore, it's better to use one of the following alternatives.
Note that, in your example, you have the entity identifier values defined in a pks variable of the List type, and I'm going to reuse that in the examples below as well.
JPQL
You can use a JPQL query like the following one:
List<Book> books = entityManager
.createQuery(
"select b " +
"from Book b " +
"where b.id in (:ids)", Book.class)
.setParameter("ids", pks)
.getResultList();
When using JPQL, The ids parameter will pass the entity identifiers in the same order they were defined in the pks variable.
Criteria API
If you want to build the query dynamically, then you can use a Criteria API query:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Book> query = builder.createQuery(Book.class);
ParameterExpression<List> ids = builder.parameter(List.class);
Root<Book> root = query
.from(Book.class);
query
.where(
root.get("id").in(
ids
)
);
List<Book> books = entityManager
.createQuery(query)
.setParameter(ids, pks)
.getResultList();
When using Criteria API, The ids parameter will pass the entity identifiers in the same order they were defined in the pks variable.
Hibernate-specific multiLoad
List<Book> books = entityManager
.unwrap(Session.class)
.byMultipleIds(Book.class)
.multiLoad(pks);
By default, the Hibernate multiLoad, The ids parameter will pass the entity identifiers in the same order they were defined in the pks variable. Only if you called enableOrderedReturn(false) explicitly, then the result set will not be ordered.
Now, the JPQL and Criteria API can benefit from the hibernate.query.in_clause_parameter_padding optimization as well, which allows you to increase the SQL statement caching mechanism.
For more details about loading multiple entities by their identifier, check out this article.

Hibernate Criteria query for a Set column

The column tests in my database looks like:
set('TEST1','TEST2', 'TEST3', ....)
I am trying to query against multiple values inside the set.
I tried doing the following:
criteria.createAlias("tests", "test");
criteria.add(Restrictions.eq("test", "TEST1"));
but got the following exception:
org.hibernate.QueryException: not an association: tests
Can't figure out how to access values from 'tests' set.
The other way I tried was the following since I need to compare multiple values inside the set but it didn't work either:
Criterion c1 = Restrictions.like("tests", EnumSet.of("TEST1"));
Criterion c2 = Restrictions.like("tests", EnumSet.of("TEST2"));
criteria.add (Restrictions.or(c1, c2));
Consider, you have created criteria as
Criteria criteria = session.createCriteria(TestCriteria.class, "testCriteria");
& TestCriteria class have property named tests. Then you can create alias for the same as
criteria.createAlias("testCriteria.tests", "test");
criteria.add(Restrictions.eq("test", "TEST1"));
From the Hibernate Docs:
Criteria createAlias(String associationPath,
String alias)
throws HibernateException
Join an association, assigning an alias to the joined association.
Functionally equivalent to createAlias(String, String, JoinType ) using
JoinType.INNER_JOIN for the joinType.
Parameters:
associationPath - A dot-seperated property path
alias - The alias to assign to the joined association (for later reference).
Returns:
this (for method chaining)
Throws:
HibernateException - Indicates a problem creating the sub criteria
Hope this helps.

How to retrieve all primary keys of a table using hibernate?

I need to retrieve all primary keys of a table and put them in a list.
Any method that I have found so far let me retrieve each record as an object, which force me to retrieve their primary keys separately and add them to the list.
Is there any other approach to retrieve the primary keys of a table and adding them to a list?
Using the following code hibernate returns objects but I need it to return a list of primary keys to store them in pk list.
List pk = new ArrayList():
Criteria criteria = session.createCriteria(MyTable.class, "mytable");
pk = criteria.list();
if mytable is as following
id name value
1 a z4
2 f o2
pk list should be
[1,2]
You can simply create an HQL query that returns the field you want:
session.createQuery("SELECT mt.id FROM MyTable mt").list();
assuming your primary key field is named id and MyTable is your entity. You can also do it with Criteria and Projections.
There are generally there ways to achieve that
1) Using Criteria API
2) Using HQL
3) Using Native Query
From the above hibernate queries way , better to us (1) and (2) , the 3rd way has dependency on the type of database.
1) Using Criteria API
Criteria criteria = session.createCriteria(MyTable.class, "mytable");
criteria.setProjection( Projections.projectionList().add( Projections.property("mytable.id"), "mytable.id"));
List<Long> ids=criteria.list();
2) Using HQL
Solution is already explained by Sotirious
3) Using Native Query
session.createSQLQuery(" SELECT mytable.id FROM MyTable mytable ").addScalar("ID","Hibernate.LONG").list();
Another approach is to use finder queries directly on repository(Spring JpaRepository) methods.
public interface MyRepository extends JpaRepository<MyTable, KeyType> {
#Query("select mt.id from MyTable mt")
List<KeyType> getAllIds();
}

can do this with JPA / Hibernate

I am working with postgres and using JPA with Hibernate, in postgres and other DBMS can do this:
SELECT *, function(parameter) AS xyz FROM table WHERE condition
My question is this, the query can display an additional field (xyz), although there is no such column. I can do that with Hibernate JPA.
If you don't have a mapped entity then you will need to use native queries:
Query query = entityManager
.createQuery(
"SELECT t.*, myfunction(:parameter) FROM table t WHERE t.attr = condition");
query.setParameter("parameter", value);
List resultList = query.getResultList();
Otherwise, if you have a mapped entity you can do this with typed queries or the criteria API.
With typed queries:
TypedQuery<Object[]> query = entityManager
.createQuery(
"SELECT t.*, FUNC('MyFunction', :parameter) FROM table t WHERE t.attr = condition",
Object[].class);
query.setParameter("parameter", value);
List<Object[]> resultList = query.getResultList();
With criteria API:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
Root<Table> root = query.from(Table.class);
Expression<Long> funcExpr = criteriaBuilder.function("myfunction",
Long.class, root.get("parameter"));
query.multiselect(query.select(root), funcExpr);
List<Tuple> resultList = entityManager.createQuery(query)
.getResultList();
By using org.hibernate.SQLQuery, you can create native SQL queries with Hibernate. You then execute these queries the same way you would with a normal Hibernate query. Obviously you're going to lose many of the benefits of letting Hibernate and JPA manage your queries, but if they do not support the feature you want, it may be the only way to do it.
Another solution is to use Formulas, if you invoke your function on some fields of the table, and you always want the result of the function you can map another property in your entity and annotate it with #Formula("myFunction(field1, field2)"). Hibernate will add this to all of your queries which have your entity.

Hibernate Critieria join two tables with condition on 2nd table and result the 1st table

I have a question using Hibernate Criteria, I need to convert this query using criteria.
SELECT * FROM A a_ INNER JOIN B b_ ON a_.column1=b_.column1 AND b_.column2 IN (X,Y) AND active='Y';
I need the result as table A.
I just solved this issue, here is my code
Criteria criteria = session.createCriteria(ProductOffer.class);
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
Date effDate = TypeConvertUtil.toDate(param.get("effDate"));
criteria.add(Restrictions.le("effDate", effDate));
criteria.add(Restrictions.gt("expDate", effDate));
criteria.createAlias("productOfferPropDefs", "def",JoinType.LEFT_OUTER_JOIN);
criteria.setFetchMode("productOfferPropDefs", FetchMode.JOIN);
criteria.add(Restrictions.le("def.effDate", effDate));
criteria.add(Restrictions.gt("def.expDate", effDate));
criteria.createAlias("def.productOfferProps", "prop",JoinType.LEFT_OUTER_JOIN);
criteria.setFetchMode("def.productOfferProps", FetchMode.JOIN);
criteria.add(Restrictions.le("prop.effDate", effDate));
criteria.add(Restrictions.gt("prop.expDate", effDate));
productOfferList = criteria.list();
Please beware that
criteria.createAlias("productOfferPropDefs", "def",JoinType.LEFT_OUTER_JOIN);
this parameter is important:
JoinType.LEFT_OUTER_JOIN
if you did not use it, and your relation is one-to-many, it will hit the 1:N classic issue for hibernate
If the associations are defined, see http://docs.jboss.org/hibernate/core/3.3/reference/en/html/querycriteria.html#querycriteria-associations
In case associations are not specified in the entities definition, you can't use criteria.
You can use HQL to do inner joins (need to write in implicit join notation), for doing left joins you have to use native SQL.

Categories