Hibernate: What is the correct way to update collection of an entity? - spring

I am trying to optimize our hibernate application. In many places, while add/updating a collection of an entity, we are clearing it first, and then add all of them again from the input. I feel somehow, there should be a better way but could not found yet.
How do you do in your applications.?

I guess with collection, you mean the "many" side of an #OneToMany relationship.
In this case, you can specify the value orphanRemoval in the #OneToMany annotation to the foreign reference in the owning class:
public class Parent {
// ...
#OneToMany(mappedBy = "whatever", orphanRemoval = true, cascade = CascadeType.ALL)
private List<Child> children;
}
orphanRemoval will instruct Hibernate to delete all existing child entities that have been removed from the collection when the parent entity is persisted.

Related

Spring/JPA twice uniderectional not working, because of #JoinColumn(nullable = false)

I have problem with unidirectional mapping and need help.
I have 2 Entities with the same unidirectional mapping.
The first one:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "massnahme_id", nullable = false)
private Set<VerortungDAO> verortungen = new LinkedHashSet<>();
The second one:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "massnahmen_verbund_id", nullable = false)
private Set<VerortungDAO> verortungen = new LinkedHashSet<>();
If I try to save one Entity, hibernate throws an Exception because of the second Entity definition (not null).
org.hibernate.PropertyValueException: not-null property references a
null or transient value
If I change the JoinColumn to nullable = true, then the unidirectional mapping not working and the list is not saved in DB.
What can I do to make it work?
Make the associations bidirectional and map the to-one association in VerortungDAO, or if you don't want that, at least map the FK-columns. If you map it bidirectional, use #OneToMany(mappedBy = "..."). Either way, you will have to initialize the two to-one associations or FK-columns on the VerortungDAO objects.
PS: An entity isn't a DAO (data access object), so the naming xxDAO is quite confusing for an entity.

Many to one mapping Hibernate

I am doing many to one mapping in hibernate. I am using the existing tables which I created earlier for one to many mapping (customer and order) but when I am trying to map and update those table I couldn't able to I don't know how should I processed? and I would like to insert the data meaning I would like to create some more orders using command line runner for that customer.
Could you please help me with this
Appreciate your help.
Mapping one-to-many and many-to-one association
Both associations are the same association seen from the perspective of the owing and subordinate entities and respectively.
Student one-to-many Address
Address many-to-one Student
#OneToMany annotation can be applied to a field or property value of "one" end entity class for a collection or an array representing the mapped "many" end of the association.
#ManyToONe relationship between two entities is by managing the FK(Foreign key) of the "one" end entity, as a column in the "many" entity table.
> **Bidirectional one-to-many using ```#JoinColumn```**
#Entity
public class Student{
#OneToMany(cascade = CasecadeType.ALL)
#JoinTable(name="Student_FK")
public set<Address> getAddress(){
return address;
}
}
One-to-Many side as the owing side, You have to remove the mappedBy element and set the #ManyToOne #JoinColumn as insertable and updatable to false. This Solution is not optimized and will produce some additional UPDATE Statement.
#Entity
public class Address{
#ManyToOne(cascade = CasecadeType.ALL)
#JoinColumn(name="STUDENT_FK", insertable = false, updatable = false)
public Student student;
}
For more details look at this link Link

Hibernate5 OneToMany and ManyToOne mapping giving null on foreign key

Yet another thread like this. I fighting with this for 4 days.
annotation #Getter and #Setter are from lombok plugin
My Place class
#Entity
public class Place {
#Getter
#Setter
#OneToMany(mappedBy = "place", targetEntity = Tag.class, cascade = CascadeType.ALL)
private Set<Tag> tags;
//...
}
Tag class which should be many
#Entity
public class Tag {
#Getter
#Setter
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "fk_place_id")
private Place place;
//...
}
I'm saving it like this
Tag tagOne = new Tag("tagOne");
Tag tagTwo = new Tag("tagTwo");
Set<Tag> tagSet = new HashSet<>();
tagSet.add(tagOne);
tagSet.add(tagTwo);
Place place = new Place();
place.setTags(tagSet);
placeService.save(place);
saving looks is the single line sessionFactory.getCurrentSession().persist(entity) on every case. Saving entity with #OneToOne mapping it works like a charm.
You have a bidirectional association. You're initializing only one side of the association, and that side is the inverse side (because it has the mappedBy attribute). Hibernate only cares about the owner side. So, for Hibernate, there is no association between the tags and the place.
Note that cascade=ALL on a ManyToXxx association doesn't make sense. If 100 tags are referencing the same place, and you delete one of these tags, you don't want to also delete the place. And even if you want to, that won't work, because 99 other tags still reference it, which will cause a referential integrity error.

Checking for multiple child constraint violations in Hibernate/JPA

TL;DR: Is it possible to perform nested transactions in Hibernate that use SavePoints to rollback to specific states?
So I am attempting to persist a parent entity with a OneToMany mapping to child entities. This is working fine.
During this persistence, I would like to catch and log ALL constraint violations that occur. Currently, the FIRST entity (child or parent) to have a constraint violation throws a ConstraintViolationException and rolls back the transaction. I would like for the transaction to still be rolled back, but somehow collect ALL of the constraint violations that would occur.
Here is a brief outline of my entities:
ParentEntity.java
#Entity
#Table(name = "PARENT", schema = "SOMESCHEMA")
public class ParentEntity {
private static final ID_COLUMN = "ID_COLUMN";
#Id
#Column(name = ID_COLUMN)
private Long id;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, orphanRemoval = true)
#JoinColumn(name = ID_COLUMN, referencedColumnName = ID_COLUMN)
private List<childEntity> children;
}
ChildEntity.java
#Entity
#Table(name = "CHILD", schema = "SOMESCHEMA")
public class ChildEntity {
public ChildEntity(String input) {
this.validationString = input;
}
#Id
#Column(name = ParentEntity.ID_COLUMN)
private Long id;
#ManyToOne
#JoinColumn(name = ParentEntity.ID_COLUMN, insertable = false, updatable = false)
private ParentEntity parent;
// The field under validation (should be less than 25 char's long)
#Column(name = "VALIDATE_ME")
private String validationString;
}
Example run:
public void someMethod() {
ParentEntity parent = new ParentEntity();
parent.addChild(new Child("good input 1"));
parent.addChild(new Child("bad input 1 break here"));
parent.addChild(new Child("bad input 2 break here"));
parent.addChild(new Child("good input 2"));
dataAccessObject.persist(parent);
}
Results:
I see the transaction rolled back and the ConstraintViolationException only contains information for the first bad child.
Desired Results:
I see the transaction rolled back and the ConstraintViolationException show information for all the bad children regardless of how many children were bad. (Also, if the parent has a constraint violation, I would still like to check the child constraints)
Is this possible?
ConstraintViolationException is a HibernateException and all Hibernate exceptions are not recoverable. So if one exception is thrown, you cannot rely on the existing session state to continue processing reliably.
So this is not possible or recommended.
I found a way to accomplish my end result, although it was through means I did not expect.
In order to accomplish my goal, I would need to use nested transactions and SavePoints. At the end of the day, that implementation would still produce invalid data in my database, because during the time it takes to find an error in a child, some of the other children may have already been persisted and a consumer of my database would be unaware that the parent and all it's children were about to be deleted due to one or more bad entities (parents or children).
My solution:
I implemented a validator to validate all of the parents and children before going to persist them. The drawback to this method is that I have to annotate constraints on my entity fields, but double validation is never a bad thing.
The answer to my original question:
This is impossible to due with my version of Hibernate, unless I implemented custom SavePoint functionality to support nested transactions.

#Transactional(readOnly = true) leads to LazyInitializationException

I have a many-to-many relation with an additional column in the link table. I've configured it in a way that the owning side fetches children eager (so I don't get LazyInitializationException) and in the opposite direction it is lazy. This works.
I now wanted to fine-tune the transactions (before there was just #Transactional on class level of DAO and Service classes. I set method getById to readOnly = true:
#Transactional(readOnly = true)
public Compound getById(Long id) {
return compoundDAO.getById(id);
}
After this change I get a LazyInitializationException in following snippet:
Compound compound = compoundService.getById(6L);
Structure structure = compound.getComposition().get(0).getStructure();
System.out.println("StructureId: "+ structure.getId()); // LazyInitializationException
If I remove (readOnly = true) this works! Can anyone explain this behavior? I use Spring + Hibernate. Kind of confusing as I don't see any reason why this should affect which data is loaded?
EDIT:
Snippets of relationship definitions. This is a many-to-many with a column in the link table.
Owning side (eg Compound contains Structures):
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.compound",
cascade = CascadeType.ALL, orphanRemoval = true)
#OrderBy("pk.structure.id ASC")
private List<CompoundComposition> composition = new ArrayList<>();
Belongs to side:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.structure",
cascade = CascadeType.ALL)
#OrderBy("pk.compound.id ASC")
private List<CompoundComposition> occurence;
Many-To-One in #Embeddable ID class
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Compound getCompound() {
return compound;
}
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
EDIT 2:
Stack Trace
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:272) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.bitbucket.myName.myApp.entity.Structure_$$_javassist_0.getId(Structure_$$_javassist_0.java) ~[classes/:na]
at org.bitbucket.myName.myApp.App.main(App.java:31) ~[classes/:na]
EDIT 3:
Also see my comment:
Log is very different with readOnly and it is missing the part were the relations are loaded, eg. some selects are missing in the log.
EDIT 4:
So I tired with a basic DriverManagerDataSource and no Connection pool. The issue is exactly the same. For me looks like an issue in Hibernate.
This is just wow. I'm starting to understand why some people hate ORMs...Just feels like I'm constantly having to spend hours to solve a weird issue and the solution is a very specific set of annotations + some code to work around the limitations of said annotations.
First to why this happens (why meaning with which annotations, but not in terms of making logical sense, which is the actual problem here as using common-sense is useless. Only trial and error helps). In the owning side, in #OneToMany I have orphanRemoval = true (which I have found out is required for consistency. one would think database constraints should handle that...just one of the many things that can drive you crazy.). It seems that if the transaction is not read-only, then this setting leads to some data being fetched even so its lazy, namely here:
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
In a read-only transaction, this fetching does not happen. I would guess because if you can't change anything you will also not have to remove orphans and hence any data that the logic behind this setting requires is not needed in a read-only tx.
So the obvious solution would be in above relation to change to FetchType.EAGER. Wrong! If you do that you will not be able to update the owning side (Compound) using session.merge. This will lead to a StackOverFlowError.
The real solution was actually already mentioned. Just leave the config as is but explicitly load the desired relations in the Service layer:
#Transactional(readOnly = true)
#Override
public Compound getById(Long id) {
Compound compound = compoundDAO.getById(id);
for (CompoundComposition composition : compound.getComposition()){
Hibernate.initialize(composition.getStructure());
}
return compound;
}
I admit I'm tending to fall in the premature optimization trap. This doesn't look very efficient and also seems to break how SQL works in the first place. But then I'm in the lucky position that in most cases CompoundComposition will contain only 1 or 2 elements.
Perhaps you could put
value.getComposition().get(i).getStructure();
in the body of the getById() method, so that the lazy loading happens within the transaction. I realize in this case you'd have to loop over i which might be inconvenient.
Two things :-
Lazy fetch works on Collections Interface. Since ...
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
... this is not a collection interface (like List<Structure> would have been), it will be fetched in Eager fetch mode.
Make service method as transactional. It seems that after fetching from the dao layer, your structure is detached with NEVER flush mode. This is the underlying ORM issue I guess.

Resources