Hibernate.initialize exception - Cutting dependency chain - java

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();

Related

Hibernate loads Lazy entities eagerly with native query

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

How stop depth join hibernate in a query?

Hi I have this 3 Class:
public class A{
#ManyToOne
#JsonIgnore
private B b;
.....
}
public class B{
#OneToOne
#JsonIgnore
private C c;
.....
}
public class C{
.....
}
I have a findById(id) on my A class, and hibernate do a join to B class, and the it do another join to C class.
But I don't need to get C class' fields, so can I do??
Thank you
You could try to set max_fetch_depth hibernate property to a properly value.
From hibernate documentation:
Sets a maximum "depth" for the outer join fetch tree for single-ended associations (one-to-one, many-to-one). A 0 disables default outer join fetching.
e.g. recommended values between 0 and 3.
But keep in mind that this will affect your entire project.
I solved as suggested:
I add #OneToOne(fetch = FetchType.LAZY) because my filed isn't mandatory..

JPA + CascadeType + cache

I have some issues with JPA cache, the scenario is as follows:
I had two classes A and B with relationship ManyToMany, but then i split the ManyToMany into two OneToMany relations and new class
public class A{
#OneToMany(cascade = CascadeType.ALL, mappedBy="a")
private List<AB> ab;
}
public class B{
#OneToMany(cascade = CascadeType.ALL, mappedBy="b")
private List<AB> ab;
}
public class AB{
#ManyToOne
#JoinColumn(name = "A_id", referencedColumnName="id")
private A a;
#ManyToOne
#JoinColumn(name = "B_id", referencedColumnName="id")
private B b;
...other fields
}
My problem is that when i delete A: i want to A and AB to be deleted, and B not touched in db (the same if i delete B).
That works, and DB state is fine but there is problem with JPA and cache.
When i remove A (and AB with cascade), JPA cache still holds references to them in B instance - what is undesirable. So when i query for B i will find nested AB instances that were removed.
I tried to put cascades on AB ManyToMany fields but it doesn't help.
If i clear cache with : entityManager.getEntityManagerFactory().getCache().evictAll(); everything works but it's not a good solution.
I use EntityManager with EJB3, and cascades from javax.persistence.
I would be grateful for answers.
Your problem is similar to eclipselink 2 OneToMany with one association table how to delete from one side . So, before deleting A (and therefore deleting AB), remove all references from B to AB.

JPA one to one relationship where entity may not exist

I've got a many-to-one mapping between two entities (A and B, one B can be associated with many As) where I need the ability to have an ID for B on A (A.B_ID) where that particular B entity doesn't exist in the database. Is this possible?
A (simplified) example of our code:
#Entity
#Table(name = "A")
public class A implements java.io.Serializable {
// ...
private B b;
// ...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "B_ID")
public B getB() {
return this.b;
}
// ...
}
#Entity
#Table(name = "B")
public class B implements java.io.Serializable {
// ...
private Set<A> as = new HashSet<A>( 0 );
// ...
#OneToMany( fetch = FetchType.LAZY, mappedBy = "b" )
public Set<A> getAs() {
return this.as;
}
// ...
}
This basic setup ends up with Hibernate trying to save a null value for A.B_ID and that's not allowed:
Caused by: java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("MY_SCHEMA"."A"."B_ID")
For clarification, if the entity doesn't already exist, I don't want it to be created. I'd just want A to be inserted with no B in the db. There is no foreign key constraint between the two tables.
I use a #NotFound annotation on the #ManyToOne side to make sure that it won't causes any errors. I haven't tried it yet with a bidirectional relationship though.
Please not that this is a Hibernate specific annotation!
Example:
#NotFound(action=NotFoundAction.IGNORE)

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