Hibernate HQL to SQL - java

Given the following entity one-to-many model:
One Repository can be linked to many AuditRecords.
Many AuditRecords can all link to the same Repository
#Entity
class AuditRecordEntity {
private AuditRepositoryEntity auditRepository;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = AUDIT_REPOSITORY_DB_COLUMN_NAME, nullable = false, updatable = false)
public AuditRepositoryEntity getAuditRepository() {
return auditRepository;
}
...
}
#Entity
class AuditRepositoryEntity {
private List<AuditRecordEntity> auditRecords = new ArrayList<AuditRecordEntity>();
#OneToMany(mappedBy = "auditRepository")
public List<AuditRecordEntity> getAuditRecords() {
return auditRecords;
}
...
}
I have the following HQL query to get the latest (by accessTime) AuditRecord for each distinct Repository:
select auditRecord from AuditRecordEntity auditRecord where auditRecord.accessTime =
(select max(auditRecord2.accessTime) from AuditRecordEntity auditRecord2 where
auditRecord2.auditRepository = auditRecord.auditRepository)
I would like to know the equivalent SQL for the above HQL?
(the reason for this is I'l like to add the query as an sql restriction using the criteria API, as I am having trouble translating the HQL above to use the criteria API - see Hibernate criteria implementation for this entity model (subquery, self-join))

There is one hibernate property named
hibernate.show_sql
You can set it true in your hibernate configuration file or property file. It will show you the equivalent sql query of your hql/criteria query.

Related

#OrderBy Hibernate: ERROR: missing FROM-clause entry for table

So I have three entities. A FormCollection contains multiple Form. The Form is created from a template and thus has also a many-to-one relation to FormTemplate.
#Table(name = "form_collection", schema = "public")
public class FormCollectionDO extends BaseAuditableDO {
#OneToMany(mappedBy = "formCollection")
#OrderBy("formTemplate.templateId") //throws error
private List<FormDO> forms = new ArrayList<>();
}
#Table(name = "form", schema = "public")
public class FormDO extends BaseAuditableDO {
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "form_template_id")
private FormTemplateDO formTemplate;
}
#Table(name = "form_template", schema = "public")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class FormTemplateDO extends BaseDO {
#Column(name = "template_id", nullable = false)
#NotNull
private Long templateId;
}
#OrderBy("formTemplate.templateId") throws an error:
o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: missing FROM-clause entry for table "formtemplate"
#OrderBy("formTemplate.id") works fine. The id comes from the abstract class BaseDO. Why does it not work with any of the fields from the FormTemplateDO class?
Although I am not sure about this solution, What I am suspecting is this issue happens because the formTemplate.templateId isnt part of your query indeed.
I see you are using #OneToMany for defining the relationship, but in hibernate, the default FetchMode is SELECT which means your order by parameter isnt part of your query. To make this parameter part of your query, you will have to make a Join query.
Try this out -
#Fetch(value = FetchMode.JOIN)
#OneToMany(mappedBy = "formCollection")
#OrderBy("formTemplate.templateId") //throws error
private List<FormDO> forms = new ArrayList<>();
And propogate this join to further levels. It might solve your problem.
When you want to order by a collection of entities by a nested attribute, you can not use #OrderBy, because the nested attribute is not part of your query. You can only use #OrderBy for of first level attribute OR a nested attribute IF it is a collection of #Embeddable.
So for this case, you have to use #SortNatural or #SortComparator.
Similar issue : Hibernate - How to sort internal query lists (or List in List)?
More about #OrderBy vs #SortNatural: Sort vs OrderBy - performance impact

Optional OneToOne relation in Hibernate

Currently working on a project where we want to extract with Hibernate the following datamodel (model is a little bit simplified). We have a class A which contains some optional data which is stored in class B
#Entity
#Data
#Table(name = "A")
public class Country {
#Id
private UUID id;
private String someCommon;
#PrimaryKeyJoinColumn
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private B details;
}
#Entity
#Data
#Table(name = "B")
public class B {
#Id
private UUID id;
private String someDetail;
}
Fetching data works fine, except that when class B is not found for some instance of A, Hibernate does an extra query for that specific instance to retrieve the details of A. I.e in the logs these are the queries executed:
select a0_.id as id1_0_0_, b1_.id as id1_1_1_, a0_.some_common as some_common2_0_0_, b1_.some_detail as some_detail_2_1_1_ from a a0_ left outer join b b1_ on a0_.id=b1_.id
select b0_.id as id1_1_0_, b0_.some_detail as some_detail_2_1_0_ from b b0_ where b0_.id=?
Where in the second query the id is set to the id of the instance which does not have details.
So it looks like Hibernate is not supporting optional OneToOne relationships in an efficient manner. Any ideas on how to force Hibernate not doing the second query but just accepting the details are null?
There is no way to get rid of second query as you mentioned in hibernate because If the association is optional, Hibernate has no way to know if an address exists for a given person without issuing a query. so closest thing you can do is to call for second query only when its intended:
So as to avoid second query you have to opt for Lazy Loading:
To do that change your mapping to set optional to false and lazy loading will be on on details:
#OneToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.LAZY)
private B details;
Lazy loading makes sure details will be fetched only when its intended.

Hibernate ignores 'lazy' fetch type and loads properties immediately

I use Hibernate 5.2.5 (also use kotlin and spring 4.3.5 if that matters) and I want some of the fields of my class to be loaded lazily. But the issue is that all fields are loaded immediately, I don't have any special Hibernate settings neither use Hibernate.initialize().
#Entity(name = "task")
#Table(name = "tasks")
#NamedQueries(
NamedQuery(name = "task.findById", query = "SELECT t FROM task AS t WHERE t.id = :id")
)
class Task {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int? = null
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "author_id", nullable = false)
lateinit var author: User
#OneToOne(fetch = FetchType.LAZY, mappedBy = "task")
var edit: TaskEdit? = null
}
This is how I query
TaskRepoImpl:
override fun findById(id: Int): Task? {
val task = getCurrentSession().createNamedQuery("task.findById", Task::class.java)
.setParameter("id", id)
.uniqueResult()
return task
}
TaskService:
#Transactional
fun find(id: Int): Task? {
return taskRepo.findById(id)
}
And the output:
Hibernate: select task0_.id as id1_1_, task0_.author_id as author_i3_1_ from tasks task0_ where task0_.id=?
Hibernate: select user0_.id as id1_3_0_, user0_.enabled as enabled2_3_0_, user0_.name as name3_3_0_, user0_.password as password4_3_0_ from users user0_ where user0_.id=?
Hibernate: select taskedit0_.id as id1_0_0_, taskedit0_.task_id as task_id3_0_0_, taskedit0_.text as text2_0_0_ from task_edits taskedit0_ where taskedit0_.task_id=?
Please advice what's wrong with my code and how to make Hibernate load properties lazily? Thank you!
Hibernate cannot proxy your own object.
There are at least three well known solutions for this problem:
The simplest one is to fake one-to-many relationship. This will work because lazy loading of collection is much easier then lazy loading of single nullable property but generally this solution is very inconvenient if you use complex JPQL/HQL queries.
The other one is to use build time bytecode instrumentation. For more details please read Hibernate documentation: 19.1.7. Using lazy property fetching. Remember that in this case you have to add #LazyToOne(LazyToOneOption.NO_PROXY) annotation to one-to-one relationship to make it lazy. Setting fetch to LAZY is not enough.
The last solution is to use runtime bytecode instrumentation but it will work only for those who use Hibernate as JPA provider in full-blown JEE environment (in such case setting "hibernate.ejb.use_class_enhancer" to true should do the trick: Entity Manager Configuration) or use Hibernate with Spring configured to do runtime weaving (this might be hard to achieve on some older application servers). In this case #LazyToOne(LazyToOneOption.NO_PROXY) annotation is also required.
#Entity
public class Animal implements FieldHandled {
private Person owner;
private FieldHandler fieldHandler;
#OneToOne(fetch = FetchType.LAZY, optional = true, mappedBy = "animal")
#LazyToOne(LazyToOneOption.NO_PROXY)
public Person getOwner() {
if (fieldHandler != null) {
return (Person) fieldHandler.readObject(this, "owner", owner);
}
return owner;
}
public void setOwner(Person owner) {
if (fieldHandler != null) {
this.owner = fieldHandler.writeObject(this, "owner", this.owner, owner);
return;
}
this.owner = owner;
}
public FieldHandler getFieldHandler() {
return fieldHandler;
}
public void setFieldHandler(FieldHandler fieldHandler) {
this.fieldHandler = fieldHandler;
}
}
Can you try this:
http://justonjava.blogspot.in/2010/09/lazy-one-to-one-and-one-to-many.html
reference:
Why Lazy loading not working in one to one association?

How to add sub-select to select

I want to execute a query like this:
SELECT Table1.COL1,
Table1.COL2,
(SELECT SUM(Table2.COL3)
FROM Table2
WHERE Table2.UID = Table1.UID) SUMOF
FROM Table1;
How can I do it?
I usually create a Criteria add ProjectionList to it, to fill COL1 and COL2 only.
I have created a DetachedCriteria to calculate the sum...
Now, how to attach this detached criteria to the main one? My intuition says - it's some sort of Projection which needs to be added to the list, but I don't see how. Also, not sure how WHERE Table2.COL4 = Table1.COL5 of detached criteria will work.
Also, I'm sure this query might be written in different way, for example with join statement. It's still interesting if there's a way to run it like this.
DetachedCriteria and main Criteria
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Table2.class, "table2");
detachedCriteria
.setProjection(
Projections.projectionList()
.add(Projections.sum("table2.col3"), "sumCol3")
)
.add(Restrictions.eq("table2.uid", "table1.uid"))
;
Criteria criteria = session.createCriteria(Table1.class, "Table1");
criteria
.setProjection(
Projections.projectionList()
.add(Projections.property("Table1.col1"), "col1")
.add(Projections.property("Table1.col2"), "col2")
)
;
Entities (very short version)
#Entity
#Table(name = "Table1")
public class Table1 {
#Id
#Column(name = "uid")
public String getUid();
#Column(name = "col1")
public String getCol1();
#Column(name = "col2")
public String getCol2();
#Column(name = "col3")
public String getCol3();
#Column(name = "col4")
public String getCol4();
#Column(name = "col5")
public String getCol5();
}
#Entity
#Table(name = "Table2")
public class Table2 {
#Id
#Column(name = "uid")
public String getUid();
#Column(name = "col3")
public BigDecimal getCol3();
#Column(name = "col4")
public String getCol4();
#Column(name = "col5")
public String getCol5();
}
For a correlated subquery (like the one you presented above), you can use #Formula which can take an arbitrary SQL query. Then, you'll need to fetch the entity and the subquery will be executed.
However, a native SQL is more elegant if you only need this query for a single business requirement.
As for derived table queries (e.g. select from select), neither JPA nor Hibernate support derived table queries for a very good reason.
Entity queries (JPQL pr Criteria) are meant to fetch entities that you plan to modify.
For a derived table projection, native SQL is the way to go. Otherwise, why do you think EntityManager offers a createNativeQuery method?

Missing rows when ordering by nested field using Criteria Api

I've encounterd weird behaviour of JPA (provider: EclipseLink) using order by functionality. I have TransactionData class, which has reference to CustomerData class:
#Entity
public class TransactionData {
//...
#ManyToOne
#JoinColumn(name = "CUSTOMER_ID")
private CustomerData customer;
//...
}
#Entity
public class CustomerData {
//...
#Column(name = "LAST_NAME")
private String lastName;
//...
}
In my project, there are some specific cases, where there are transactions, which are not assigned to any customer (called non-customer transactions).
I try to get list of all registered transactions (both cusotmer transactions and non-customer transactions) and sort them by customer's last name. To acheive this I've written following Criteria Api
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<TransactionData> criteriaQuery = criteriaBuilder.createQuery(TransactionData.class);
Root<TransactionData> from = criteriaQuery.from(TransactionData.class);
criteriaQuery.orderBy(criteriaBuilder.asc(from.get("customer").get("lastName"));
TypedQuery<TransactionData> query = entityManager.createQuery(criteriaQuery);
return query.getResultList();
I think I should get list of all transactions, of course, with those, where customer field is set to NULL value. But JPA behaviour is different, because it cut out all transactions, where reference to customer is empty.
from.get("customer").get("lastName") will do implicitly an INNER JOIN. If some transactions have no customer assigned then what you need is a LEFT JOIN:
Join<TransactionData , CustomerData> customer = from.join("customer", JoinType.LEFT);
criteriaQuery.orderBy(criteriaBuilder.asc(customer.get("lastName"));

Categories

Resources