Differences between JPA predicate using Entity and property - java

Say I have the following Entity classes:
#Entity public class MyEntity {
#Id private String id;
#ManyToOne private MyOtherEntity myOtherEntity;
}
#Entity public class MyOtherEntity {
#Id private String id;
#Column private String name;
}
Now I want to do a query to get all the MyEntitys linked to a certain MyOtherEntity, I wonder the difference between the following 3 predicates:
cb.equal(root.get(MyEntity_.myOtherEntity), myOtherEntity);
cb.equal(root.get(MyEntity_.myOtherEntity).get(MyOtherEntity_.id), myOtherEntity.getId());
cb.equal(root.get(MyEntity_.myOtherEntity).get(MyOtherEntity_.name), myOtherEntity.getName());
How would the generated SQLs look like in each case? And which one is most efficient?

For a start I suggest to take the trouble and enable SQL logging in Hibernate while developing - see here. Knowing the exact statements Hibernate creates for your JPA queries is invaluable, e.g. you have a chance to spot N+1 query problems, excessive joins etc.
Having said that, in your case the statements should look like as follows:
cb.equal(root.get(MyEntity_.myOtherEntity), myOtherEntity) → SELECT ... FROM MyEntity WHERE MyEntity.myOtherEntity_id = ?. In cases like this, Hibernate usually knows to optimize and avoid the unnecessary join.
cb.equal(root.get(MyEntity_.myOtherEntity).get(MyOtherEntity_.id), myOtherEntity.getId()) → Should be like above; again Hibernate should know that the .get(MyOtherEntity_.id) is already in the table and avoid the unnecessay join.
I have seen Hibernate working the way I describe for the cases above. Definitely enable SQL logging to verify, there may be details for your own use case that make it behave in a different way!
cb.equal(root.get(MyEntity_.myOtherEntity).get(MyOtherEntity_.name), myOtherEntity.getName()) → Will definitely create a join because it cannot find myOtherEntity.name in the MyEntity table: SELECT ... FROM MyEntity e JOIN MyOtherEntity oe ON ... WHERE oe.name = ?

Related

Hibernate is making extra SQL statement with #ManyToOne and #Lazy fetching object

I would like someone to explain me why Hibernate is making one extra SQL statement in my straight forward case. Basically i have this object:
#Entity
class ConfigurationTechLog (
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long?,
val configurationId: Long,
val type: String,
val value: String?
) {
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "configurationId", insertable = false, updatable = false)
val configuration: Configuration? = null
}
So as you can see, nothing special there. And when i execute this query :
#Query(value = "SELECT c FROM ConfigurationTechLog c where c.id = 10")
fun findById10() : Set<ConfigurationTechLog>
In my console i see this:
Hibernate:
/* SELECT
c
FROM
ConfigurationTechLog c
where
c.id = 10 */ select
configurat0_.id as id1_2_,
configurat0_.configuration_id as configur2_2_,
configurat0_.type as type3_2_,
configurat0_.value as value4_2_
from
configuration_tech_log configurat0_
where
configurat0_.id=10
Hibernate:
select
configurat0_.id as id1_0_0_,
configurat0_.branch_code as branch_c2_0_0_,
configurat0_.country as country3_0_0_,
configurat0_.merchant_name as merchant4_0_0_,
configurat0_.merchant_number as merchant5_0_0_,
configurat0_.org as org6_0_0_,
configurat0_.outlet_id as outlet_i7_0_0_,
configurat0_.platform_merchant_account_name as platform8_0_0_,
configurat0_.store_type as store_ty9_0_0_,
configurat0_.terminal_count as termina10_0_0_
from
configuration configurat0_
where
configurat0_.id=?
Can someone please explain me, what is happening here ? From where this second query is coming from ?
I assume you are using Kotlin data class. The kotlin data class would generate toString, hashCode and equals methods utilizing all the member fields. So if you are using the returned values in your code in a way that results in calling of any of these method may cause this issue.
BTW, using Kotlin data claases is against the basic requirements for JPA Entity as data classes are final classes having final members.
In order to make an association lazy, Hibernate has to create a proxy instance instead of using the real object, i.e. it needs to create an instance of dynamically generated subclass of the association class.
Since in Kotlin all classes are final by default, Hibernate cannot subclass it so it has to create the real object and initialize the association right away. In order to verify this, try declaring the Configuration class as open.
To solve this without the need to explicitly declare all entities open, it is easier to do it via the kotlin-allopen compiler plugin.
This Link can be useful for understand what kind (common) problem is that N + 1 Problem
Let me give you an example:
I have three Courses and each of them have Students related.
I would like to perform a "SELECT * FROM Courses". This is the first query that i want (+ 1) but Hibernate in background, in order to get details about Students for each Course that select * given to us, will execute three more queries, one for each course (N, there are three Course coming from select *). In the end i will see 4 queries into Hibernate Logs
Considering the example before, probably this is what happen in your case: You execute the first query that you want, getting Configuration Id = 10 but after, Hibernate, will take the entity related to this Configuration, then a new query is executed to get this related entity.
This problem should be related in specific to Relationships (of course) and LAZY Fetch. This is not a problem that you have caused but is an Hibernate Performance Issue with LAZY Fetch, consider it a sort of bug or a default behaviour
To solve this kind of problem, i don't know if will be in your case but ... i know three ways:
EAGER Fetch Type (but not the most good option)
Query with JOIN FETCH between Courses and Students
Creating EntityGraph Object that rappresent the Course and SubGraph that rappresent Students and is added to EntityGraph
Looking at your question, it seems like an expected behavior.
Since you've set up configuration to fetch lazily with #ManyToOne(fetch = FetchType.LAZY), the first sql just queries the other variables. When you try to access the configuration object, hibernate queries the db again. That's what lazy fetching is. If you'd like Hibernate to use joins and fetch all values at once, try setting #ManyToOne(fetch = FetchType.EAGER).

insert into select with jpa error

I'm trying to do an insert into select with Jpa.
The Entity on which I try to do it is like this:
#Entity
public class A {
private String fieldOne;
private String fieldTwo;
private String fieldThree;
private B fieldFour;
#Id
public String getFieldOne(){...}
#Id
public String getFieldTwo(){...}
#Id
#OneToOne
public B getFieldThree(){...}
public String getFieldFour(){...}
....
#Entity
public class B {
private CompositeId id;
....
#EmbeddedId
public CompositeId getId(){
return MyUUIDGenerator.generateCompositeId();
}
....
The insert I'm trying to is very simple:
insert into A (fieldOne, fieldTwo, fieldThree, fieldFour)
select 'staticValueOne', 'staticValueTwo', B.id, 'staticValueFour' from B
where ....
The 'staticValueX' are values calculated by the application that I need to be all equals for a given set of B elements.
During execution the application return the exception:
java.lang.IllegalArgumentException: org.hibernate.QueryException: can
only generate ids as part of bulk insert with either sequence or
post-insert style generators [insert into ...
I don't understand why, because I don't have any generated value in A, I give to the insert all the values it need.
Does anyone has a suggestion to understand this behaviour?
Thanks!
EDIT: a little update...
I changed the class A with only a field of String type marked as #Id, but hibernate makes errors in building correctly the query: the association of tables alias with fields name miss some fields.
From JPA 2.0 specification, chapter 4.2 Statement Types:
A Java Persistence query language statement may be either a select
statement, an update statement, or a delete statement. (...)
In BNF syntax, a query language statement is defined as:
QL_statement :: = select_statement | update_statement | delete_statement
Instead of SELECT statement which is not supported in JPA (either in JPQL or Criteria API) use ElementManager.persist on an entity within a transaction. When transaction commits the entity is written to the database (SQL INSERT will be done implicitly by Hibernate which acts as the persistence provider).
EDIT: In case of a large number of insertions you may take a closer look at Hibernate's batch inserts. Another option is to give up with JPA and use JDBC's batch insertion (PreparedStatement) directly.

hibernate criteria. self join for denormalized table

Hello all and sorry for my English =)
I works with Hibernate using criteria API. Everything was fine, but I have several denormalized tables with data for reports, and I faced some troubles.
For one of that tables I created #Entity class for mapping like
#Entity
#Table(name= "table")
public class Report {
#Id
Integer id;
Integer product_id;
Integer warehouse_id;
String some_data;
}
with simple queries all works fine. But I need to make queries like pivot table or self join etc.
for example
select
t1.product_id,
t2.warehouse_id
from repost t1
join report t2
on t1.product_id = t2.product_id
and t1.warehoise_id = ?
where t1.some_data in (?)
Such query does not contain logical dependencies between entities like Primary_Key - Foreign_Key and can return custom object data (it can be Map<>, List<>, Pair<>, Long...)
Is it possible to make query like this without using HQL?
Thanks
ADDED
As I was understood it's not possible using HQL too

JPA Query For Exists In

I have the following Entities (reduced and renamed for this example)
#Entity
public class Case {
#Id
private Long id;
#ManyToOne(optional=false)
private CourtConfiguration courtConfiguration;
#ElementCollection(fetch=FetchType.EAGER)
private List<String> caseNumbers;
}
Second Entity
#Entity
public class CourtConfiguration {
#Id
private Long id;
String countyId;
String referenceId;
....
}
I am trying to search using JPQL for all Cases that have a certain courtConfiguration countyId and have caseNumbers containing all of a provided set of important caseNumbers.
So my query needs the countyId and set of caseNumbers as parameters. Called countyId and importantCaseNumbers respectively.
I have tried and failed to get it to work.
My query looks like this
String query = "SELECT case FROM Case case JOIN case.caseNumbers caseNumbers WHERE ";
query += "case.caseConfiguration.countyId = :countyId ";
The bit above works until I add my caseNumber conditions.
I have tried a foreach importantNumbers to extend the query and as soon as the list of important numbers goes above one it doesn't work. No values get returned.
for (String importantCaseNum : importantCaseNumbers) {
query += " AND '"+importantCaseNum+"' in (caseNumbers)";
}
Any suggestions/pointers appreciated. I guess what I am looking for is a case.caseNumbers contains (importantNumbers) clause.
Update I have reverted to native SQL for my query as I didn't want to tie myself into hibernate by using HQL. Thanks to #soulcheck and #mikko for helping me out. I'll post up when the hibernate JPA fix is available.
Thanks
Paul
Syntactically correct way to build this JPQL query is with MEMBER OF. But because of problem reported in HHH-5209 it doesn't work with old Hibernate versions (fix version 4.1.8, 4.3.0.Beta1). According bug report HQL version of this query works, so your options includes at least:
Using JPQL query and switching to some other JPA implementation
Using HQL instead and sticking with Hibernate:
query += " AND '"+importantCaseNum+"' in elements(caseNumbers)";

How can get JPA to use an outer-join on a condition involving an associated entity?

I have two JPA entities:
#Entity
public class TaskSchedule {
...
private String name;
...
}
#Entity
public class Task {
...
private String description;
#ManyToOne
private TaskSchedule taskSchedule;
...
}
I would like to have a query that looks like this:
select t
from Task t
where t.description like '%text%' or t.taskSchedule.name like '%text%'
Not all Tasks have a TaskSchedule. The above JPQL query generates an inner-join in the resulting SQL, which excludes all of the tasks without a TaskSchedule.
How can I tell JPA to perform an outer-join in the generated SQL?
I am using Hibernate EntityManager as the JPA implementation.
Thanks,
Dave
You can have an outer join in JPQL like you do in SQL, through the LEFT [OUTER] JOIN keywords. The JPQL Language Reference has a pretty good example of this.
Freehanding this, the finished JPQL will probably look similar to:
select t
from Task t left outer join t.taskSchedule ts
where t.description like '%text%' or ts.name like '%text%'

Categories