Hibernate loads Lazy entities eagerly with native query - java

I was trying to optimise number of calls for my schema creational endpoint and get rid of n+1 problem. And I actually did it replacing them with only 2 calls. I have achieved it with Named Queries but I wonder if it is possible to do it with nativeQuery=true.
So, here is the situation:
Class A:
#Entity
#IdClass(AId.class)
#Table(name ="A")
#Data
class A implements Serializable{
#Id
#Column(name = "ID")
private Integer id;
// code omitted...
#ManyToOne(fetch=FetchType.Lazy)
#JoinColumns(
...
)
private B b;
}
Class B:
#Entity
#IdClass(AId.class)
#Table(name ="B")
#Data
class B implements Serializable{
#Id
#Column(name = "ID")
private Integer id;
// code omitted...
#OneToMany(mappedBy="b", cascade=Cascade.ALL, fetch=FetchType.Lazy)
#JoinColumns(
...
)
private List<A> aList;
}
The problem occurs when I try to fetch the data with generated or native queries.
It grabs B entities when I fetch A though it's Lazy for A.
Expected behaviour is the executing a query as follows:
Hibernate:
/* dynamic native SQL query */ SELECT
*
FROM
A a
WHERE
a.ID IN (
?
)
But right after this (no matter how I played with dynamic native query) I have one more query for B entity that I don't need:
Hibernate:
/* load com.example.to.B */ select
b0_.id as id1_1_0_,
from
B b0_
where
b0_.id=?
Why is this so and is there any way to avoid it?
Thanks in advance.

Set a breakpoint in e.g. org.hibernate.resource.jdbc.internal.EmptyStatementInspector#inspect to see which code triggers the lazy initialization. Maybe it's your debugging that triggers this through a toString implementation.

Hibernate cannot fetch lazily for ToOne relationships.
The field b can be null in A, and hibernate has to check with the table b before confirming to populate the field with null. The query for B you observed is the result.
You may consider using #MapsId, e.g.:
#ManyToOne
#MapsId(“id”)
private B b;
You may read further about #MapsId here:
https://www.objectdb.com/api/java/jpa/MapsId

Related

Joining two table using hibernate in spring jpa

I want to write a join query for joining two tables in Spring Data, using Hibernate.
sql: select * from a join b on a.B_Id=b.id;
My classes are as below:
In class A, I don't have reference of B, I have only B_id. Since its a large project I can't change the class definitions. But need to migrate the join queries to Hibernate.
Any way I can do it?
#Entity
#Table(name = "a")
public class A {
#Id
private Integer id;
// ...
private Integer B_Id;
}
#Entity
#Table(name = "b")
public class B {
#Id
private Integer id;
// ...
}
Many to one mapping -> many A can have the same B.

How to join two tables using Criteria API if join relationship is on EmbeddedId

I have the following classes:
#Entity
public class EventOrderLine {
#EmbeddedId private EventOrderLineId id;
}
#Embeddable
public class EventOrderLineId implements Serializable {
#ManyToOne
#JoinColumn(name = "eventid")
#JsonIgnore
private Event event;
#ManyToOne
#JoinColumn(name = "orderlineid")
#JsonIgnore
private OrderLine orderLine;
}
#Entity
public class OrderLine {
#OneToMany
#JoinColumn(name = "orderlineid")
#JsonIgnore
private List<EventOrderLine> eventOrderLines = new ArrayList<>()
}
Basically I'm trying to join the two tables via the Criteria API but having issues since this is what I want to do:
Root eventOrderLine = criteriaQuery.from(EventOrderLine.class);
Join orderLine = eventOrderLine.join("orderLine");
Of course this give me this issue since the mapping isn't directly on the entities themselves:
Unable to locate Attribute with the the given name [orderLine] on this ManagedType [com.EventOrderLine]
I've been trying to tweak the join to drill into the embeddedId but not sure if I need to go a step further and modify how my entities are mapped. I feel like it's probably something simple I'm missing but having trouble finding this specific question.
The event field is a member of EventOrderLineId and not EventOrderLine. In your criteria query, you first need to navigate to id. The catch is that Root.path("id") returns an instance of Path, which does not allow further joins.
The trick is to use a 'fake' join with the id field like so: eventOrderLine.join("id").join("event")
eventOrderLine.get("id").get("event") would likely work just as well, but it wouldn't allow you to specify the join type.
first try to get the property id of EventOrderLine entity and then join. So, it would be -
Root eventOrderLine = criteriaQuery.from(EventOrderLine.class);
Join orderLine = eventOrderLine.get("id").join("orderLine")

Hibernate.initialize exception - Cutting dependency chain

I have a Class A which has an object of Class B which has an object of Class C. I want to get object of class B from object of class A without getting object of class C in b. (I have , and want to keep it this way, everything with lazy loading)
I am doing:
Hibernate.initialize(a.getObjectOfClassB());
But get exception. Is there any way to do what i want? Cutting the hibernate initialize chain?
Thanks in advanced!
So your entity structures appear to be mapped as follows:
public class EntityA {
#OneToMany(mappedBy = "a")
private List<EntityB> bList;
}
public class EntityB {
#ManyToOne
private EntityA a;
#OneToMany(mappedBy = "b")
private List<EntityC> cList;
}
public class EntityC {
#ManyToOne
private EntityB b;
}
So you have a specific EntityA that you want to fetch it's associated EntityB instances. You can obtain that list either at query time or as a post initialization step.
The important thing to note here is that the mappings between A - B - C are using #OneToMany which are lazily fetched by default.
To do this at query time:
SELECT a
FROM EntityA a
JOIN FETCH EntityB b
WHERE a.id = :entityAId
The returned EntityA already has your List<EntityB> already loaded for you and you need to do nothing else.
To do this as a post initialization step after you've fetched a single EntityA instance.
Hibernate.initialize(entityA.getBList());
or
entityA.getBList().size();

HQL delete associations from many to many mapping

I Have two entities CRImageType & CRVariable with a many to many relation as follows:
CRImageType entity:
#Entity
#Table(name = "imageviewer_crimagetype")
public class CRImageType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "ImTypeId")
private Long imTypeId;
#Column(name = "ImTypeName")
private String imTypeName;
#Column(name = "ImTypeDescription")
private String imTypeDescription;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="imageviewer_imtype_variable",
joinColumns={#JoinColumn(name="ImTypeId")},
inverseJoinColumns={#JoinColumn(name="VarId")})
private Set<CRVariable> crvariables = new HashSet<CRVariable>();
CRVariable entity:
#Entity
#Table(name = "imageviewer_crvariable")
public class CRVariable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "VarId")
private Long varId;
#Column(name = "VarName")
private String varName;
#Column(name = "VarDescription")
private String varDescription;
#ManyToMany(mappedBy="crvariables")
private Set<CRImageType> crimagetypes = new HashSet<CRImageType>();
In my database the relation is mapped by two tables "imageviewer_crimagetype" & "imageviewer_crvariable" and a third one "imageviewer_imtype_variable" for their many to many relation.
I would like only to DELETE association records from table "imageviewer_imtype_variable". How can be done using an HQL query since i can not directly access "imageviewer_imtype_variable table.
I would like the HQL equivalent of an SQL query like
delete from imageviewer_imtype_variable where ImTypeId='%%%'
This is JPA, not Hibernate specifically. The fact that you have a standardized API on top here makes it easier to find answers if you search in the context of the API, not the implementation.
The way to do it (as far as I remember, I don't use many to many relationships that often) is to remove the related entities from each other's collection mapping fields. So if you have EntityA and EntityB, you remove EntityA from EntityB and EntityB from EntityA. The persistence provider should then be triggered to remove the record from the join table.
Native queries should only be a last resort IMO.
You can execute a native SQL query:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch18.html#querysql-creating
So in your case, something like:
session.createSQLQuery("DELETE FROM imageviewer_imtype_variable").executeUpdate();
You can also specify a custom native SQL DELETE query within your CrImageType entity:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch18.html#querysql-cud
From my experience, handling the ManyToMany relation is one of the few case where getting out of ORM integrism is the best option for our mind's sake. Particularly, when you use a two way navigation (i.e. when the relation and the inverse relation are usefull).
#Gimby is correct in his answer, the thing is that with a complex cross referencing it is far harder to make it work, than doing a simple native query.
So:
session.createSQLQuery("DELETE FROM imageviewer_imtype_variable").executeUpdate();
is easier, if it still does not work because of cross referencing, you migh even add a :
session.clear();
OK. This is another ORM integrism infringment, but get you out of the cesspit in two lines ;-) .
The answer from #Gimby is the correct one, in a many to many relationship removing related instances from each other mapping collection triggers a delete from the relationship mapping table.
In the entity class CRVariable you add :
#PreRemove
private void removeCRVariableFromCRImageType() {
for (CRImageType crImageType: CRImageType) {
crImageType.getCrvariables ().remove(this);
}
}
This method will override the JPA action PreRemove in order to detach the CRVariable object to be removed from the set crvariables (technically the table imageviewer_imtype_variable)
Hope this helps you !

Eclipselink lazy loading issue for objects with discriminator column

We have following hierarchy in our application:
#MappedSuperclass
public abstract class AbstractDemandOrMeasureBE {
}
#Entity
#Inheritance
#DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.INTEGER)
#Table(name = "V_VIEW2")
public abstract class AbstractDemandOrConcreteMeasureBE extends AbstractDemandOrMeasureBE {
#Column(name = "VC_ID")
private Long vcId;
}
#Entity
#DiscriminatorValue("2")
public class MinimalDemandBE extends AbstractDemandOrConcreteMeasureBE {
..
}
#Entity
#DiscriminatorValue("1")
#HasRelationsAnnotatedAsLazyLoaded
public class ValidationMeasureBE extends AbstractDemandOrConcreteMeasureBE {
..
}
In other object I am trying to load those entities like that:
#Table(name = "V_VIEW2")
public class VCBE extends SomeVeryAbstractBE {
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "VC_ID")
private List<ValidationMeasureBE> validationMeasures;
public transient static final String ATTRIBUTE_VALIDATION_MEASURES = "validationMeasures";
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "VC_ID")
private List<MinimalDemandBE> minimalDemands;
public transient static final String ATTRIBUTE_MINIMAL_DEMANDS = "minimalDemands";
There is a precompiled query to load all hierarchy, which load some other parent objects. There is also a hint for the query - eclipselink.left-join-fetch=PP.VCBE.validationMeasures (if this is changed to eclipselink.left-join-fetch=PP.VCBE.minimalDemands, then minimal demands are loaded, but validation measures (entries with discriminator 1) are also loaded into the minimal demands collection - but those should not be loaded).
Now, when query is executed validationMeasures collection if filled with objects, but all those object are actually minimal demands and have 2 as a discriminator value in the database.
The query, which gets executed is following:
SELECT * FROM V_VIEW1 t1
LEFT OUTER JOIN V_VIEW0 t0 ON (t0.PP_D = t1.ID)
LEFT OUTER JOIN V_VIEW2 t2 ON (t2.VC_ID = t0.ID)
WHERE (((t1.ID = ?) AND (t1.HP_ID = ?))
AND t1.HP_IS IN (SELECT t3.ID FROM V_VIEW t3 WHERE (t3.HWPG_ID = ?)))
bind => [3 parameters bound]
As I can see there is no DISCRIMINATOR constraint in the query, why?
Any ideas of such a behavior? And how can I tell eclipselink to load collection, depending on discriminator value?
Can you include the JPQL query and hints you use to get this SQL.
So, you are saying it works when you use a join-fetch, but not a left-join-fetch?
This seems like it may be a bug, that the inheritance discriminator expression is not being included when using an outer join. If this is the case, please log a bug for this and vote for it.
Your model is very odd though. Why split the two subclasses into two separate relationships? Having one would be much more efficient. Or if you do split them, you should be using different foreign keys, not the same one. Sharing the same foreign key for two different relationships is probably not a good idea.

Categories