Hibernate LAZY fetch not initializing an instance - java

I encountered a problem with lazy association two days ago and still haven't found an explanation of such a behavior.
Here is my simplified class hierarchy:
#Entity
#Table(name="A")
public class A
{
#Id
#GeneratedValue
#Column(name="ID")
private int id;
#OneToMany(mappedBy="a", fetch=FetchType.LAZY, cascade=CascadeType.ALL)
private Set<B> listB = new HashSet<B>();
}
#Entity
#Table(name="B")
public class B
{
#Id
#GeneratedValue
#Column(name="ID")
private int id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="A_ID")
private A a;
#ManyToOne(fetch=FetchType.LAZY) // ERROR!
// #ManyToOne(fetch=FetchType.EAGER) // OK
#JoinColumn(name="C_ID")
private C c;
}
#Entity
#Table(name="C")
public class C {
#Id
#GeneratedValue
#Column(name="ID")
private int id;
}
When I try to read simple structure from db: A->B->C i get the following results:
System.out.println(a.getId()); // 1
for (B b : a.getListB()) {
System.out.println(b.getId()); // 1
C c = b.getC();
System.out.println(c.getId()); // 0 !!!
}
As you can see instance of C is not properly initialized.
After changing fetch type from LAZY to EAGER for field c in class B everything works!
I suspect there is is some CGLIB magic, but can't find a clue nether in the specification nor in Google.
Could someone explain this?
Thanks for any help!!!

In case you would like to understand lazy loading see this answer; it defines it pretty well.

Fixed. My question was not correct. Sorry.
There was final modifier for getters and setters of my entitiy classes. It breaks single value associations like many-to-one.
Suppose I've missed some warnings in hibernate logs...
I think it would be better to throw an exception by hibernate for this case.

Related

JPA: Mapping crossed OneToOne and ManyToOne relations

I have two entities, which we'll call A and B. B always has A as a parent with a ManyToOne relation.
However, I need A to have a OneToOne relation with the latest record inserted in table B.
This is because I need to save multiple versions of B but 99% of the time will only need to use the most recent one.
This looks something like this:
#Data
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#OneToOne(optional = false)
private B latest;
}
#Data
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#Column(nullable = false)
private Date lastModified;
#ManyToOne(optional = false)
private A parent;
}
Now, the issue at hand is that I cannot seem to persist these entities as one always appears to be transient:
A cannot be persisted because latest references B, yet B is not persisted.
B cannot be persisted because parent references A, yet A is not persisted.
Attempting to do so results in:
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : B.parent -> A
I tried wrapping the code responsible for persisiting them in a #Transactional method but the same happens:
#Transactional
public void saveAB(A parent, B child) {
parent.setLatest(child);
child.setParent(parent);
Arepository.save(parent);
Brepository.save(child);
}
I also thought of disregarding the OneToOne relation from A to B, instead having latest as a transient #Formula field which would query B to take the most recent record. However, #Formula seems to be limited to primitives, not full entities.
What would be the proper way to do this with JPA? Am I approaching this the wrong way?
Since A and B depend on each other they should probably be considered a single aggregate with A being the aggregate root.
This means you'd have only an ARepository and also CascadeType.ALL on the relationships.
The solution was to apply #JoinFormula as explained here.
#Data
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#ManyToOne
#JoinFormula(value = "(SELECT b.id FROM b " +
"WHERE b.id = id ORDER BY b.lastModified DESC LIMIT 1)")
private B latest;
}
Then on B:
#ManyToOne(optional = false)
private A parent;

Specifications JPA - Inner + where clause

I have to join two tables and filter the result with a Where with Specifications and JPA. But I'm not very familiar with it.
The tables are for example:
public class A {
private Long id;
private Long secondId;
...
}
public class daughterA {
#JoinColumn (name = "id")
private Long idA;
...
}
and the WHERE is to be applied on secondId.
Thank you all for the help, I apologize for any inaccuracies, i'm beginning with these technologies.
First of all, your Join seems to be a one To Many join so you should make it as it is :
add the One annotation in the parent class being mapped by the name of the instance of it in the other class, this notation covers a Set or a List of objects of the daughters class
add the ManyToOne annotation in the daughter class, a daughter can only have one mother, and idA is not Long but it refers to the parent class it's in fact a foreign key
public class A{
#Id
private Long id;
private Long secondId;
#OneToMany(mappedBy="idA")
     private Set<daughterA > daughters;
...
}
public class daughterA {
#ManyToOne
#JoinColumn (name = "id")
private A idA;
...
}
and for the where clause :
TypedQuery<Country> query =
em.createQuery("SELECT d FROM daughterA d WHERE d.idA.secondId = :secondId ", Country.class);
A results = query.setParameter("secondId",sencondId).getSingleResult();

Hibernate Enver : #AuditMappedBy is not working

I created an instance of A, defined name, with a blank collection of entities B and save it into DB. This is revision #1. Now I use the following statement to get all initial revision of class A
//Get revisions
A a = auditReader.find(A.class, aId, revisions.get(0));
I am getting an exception
could not resolve property: aId_id of: .B_AUDIT [select e__ from B_AUDIT e__ where e__.aId_id = :a_id and e__.originalId.REV.id <= :revision and REVTYPE != :delrevisiontype and (e__.REVEND.id > :revision or e__.REVEND is null)]
Following are my class details
#Table(name = "A")
#Audited
public class A{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
....
#OneToMany(mappedBy = "aId")
#AuditMappedBy(mappedBy = "aId")
private List<B> b;
}
which has #oneToMany relationship with B
#Entity
#Table(name = "B")
#Audited
public class B{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int aId;
...
}
Hibernate Enver Version : 5.1.4.Final
Thank you for your support.
If I had to wager a guess, I believe it is likely because of how you decided to map the oppsite side of the #OneToMany relationship inside entity B. You mapped it directly to the primary key value rather than to the entity type itself.
In other words, Envers likely expected this mapping instead:
#Entity
#Table(name = "B")
#Audited
public class B {
// other stuff removed for breavity
#ManyToOne
private A a;
}

setting an id autogenerate into an object

Sorry if my post is duplicated or the tittle doesn't describe the topics, because I don't know how to describe this in the tittle, I look on internet, but I didn't find the solution.
I am using Java and JPA. The problem is the next :
I have a class A with an autogenerated key :
class A{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private List<B> listB;
}
And the class B with the id of this clas:
class B {
#EmbeddedId
private Bid id;
private String att;
}
class Bid {
private int idA;
private String text;
}
In a controller I want to create an object A, the problem is when I created the object A, I need to create the object B where the id of B contains the id of A which is autogenerated, and it is created in the moment when the entity is mapped to de database, I dont't know how to set the id autogenerated of A into the idB, maybe I should query to de database asking what is the las id of classA, but it seem bad.
Thanks in advance
Your case is a derived identifier case, where your entity B's identity was derived from the primary key of A. You can use #MapsId annotation for this case and your entities can be restructured like this:
#Entity
public class A {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#OneToMany(mappedBy="a")
private List<B> listB = new ArrayList<B>();
...
}
#Entity
public class B {
#EmbeddedId
private BId id;
#ManyToOne
#MapsId("idA")
private A a;
...
}
#Embeddable
public class BId {
private int idA;
private String att;
...
}
This is how you would persist the entities:
A a = new A();
BId bid = new BId();
bid.setAtt("text"); // notice that the idA attribute is never manually set, since it is derived from A
B b = new B();
b.setId(bid);
b.setA(a);
a.getListB().add(b);
em.persist(a);
em.persist(b);
See sample implementation here.
It would be useful to know which is the case scenario you are trying to solve in general because the structure you are using seems unnecessarily complex.
What is your real goal?

hibernate, stackoverflow with particular entity mapping

I have the following mapping:
#Entity
public class Satellite implements Serializable, Comparable<Satellite> {
#NotNull #Id
private long id;
.....
#OrderColumn
#OneToMany(mappedBy = "satellite", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<DataModel> dataModel;
}
and a child entity:
#Entity #IdClass(value=DataModelPK.class)
public class DataModel implements Serializable, Comparable<DataModel> {
private static final long serialVersionUID = -3416403014857250990L;
#Id
private int orbit; // related to reference orbit file
private int dataPerOrbit; // in Gbit
#ManyToOne #Id
private Satellite satellite;
}
originally, DataModel was an embeddable entity, but for a better control over the primary key and the underlying structure of the db, I switched to a more traditional model.
The point is, during the loading of the entity now it generate a stack overflow!! I think there is some cyclic loading between those two entities and it got stuck!
I'm thinking to revert everything back to what it was, but I wish to understand why it gives me this error.
You have #IdClass for DataModel specified to be DataModelPK.class but your #Id annotation is on an int field.
This is a problem, it may be causing you stackoverflow but I am not certain.
Update I now see the second #Id annotation so I stand corrected, I will investigate furtuer.

Categories

Resources