Spring data jpa updatedOn getting changed for all the entities in OneToMany mapping while parent entity is saved - jpa

I am having a OneToMany Relationship in one of my Entity like below and using SpringDataJpa for data access layer.
#Entity
public class StudentSkillRecord {
...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.EAGER)
#JoinColumn(name = "skill_id")
private List<SkillAssessment> skillAssessments = new ArrayList<SkillAssessment>();
}
Every time a new SkillAssessment is added or one of the column of the SkillAssessment is changed, I am persisting the whole StudentSkillRecord.
However the updatedOn column is getting updated for all the entities of the collection skillAssessments even though I modify only one of them.
Is there a way, I can update only for new SkillAssessment or modified ones?
I am using EntityListiner for SkillAssessment to change the audit value like below:
#PrePersist
#PreUpdate
public void onPreMerge() {
this.updatedOn = new Date();
}

Related

How to audit changes in OneToMany relationship into owning entity with Spring Data JPA?

I have successfully set up JPA auditing in an entity. It works fine provided that there is a change in the data of the entity itself. However, the entity contains a #OneToMany collection, and I'd like the owning entity's #LastModifiedBy field to also get updated when only the child rows change. I don't want to add auditing fields to each row, if possible.
Definition of relationship in parent:
#Entity
#EntityListeners(AuditingEntityListener.class)
public class DataHead {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#CreatedBy
private String creatorUser;
#CreatedDate
private Date createdDate;
#LastModifiedBy
private String modifierUser;
#LastModifiedDate
private Date modifiedDate;
#OneToMany(mappedBy="dataHead", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval=true)
#Fetch(FetchMode.SUBSELECT)
#Valid
private List<DataRow> rows = new AutoPopulatingList<DataRow>(DataRow.class);
...
}
Tried to add #EntityListeners to the child entity, didn't help.
I'm using spring-data-jpa 1.9.4 with Hibernate 4.3.11 as implementation.
As workaround, you can try to add additional field to table and annotate it by JPA #Version.
#Version
#Column
private int version;

jpa repository delete children without loading parent collection

I am trying to delete a Child Entity using Child's Repository. I do not want to load the whole Collection of Child in Parent and remove a Child from there because the collection is huge in some cases and can cause memory issues. But after I delete a child when I load the Parent using Parent Repository I get an error that says "Deleted Entity passed to persists".
#Entity
#Table(name="USR")
public class User {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval=true)
private Set<UserApplication> userApplications = new HashSet<UserApplication>();
}
#Entity
#Table(name="USR_2_APL")
public class UserApplication {
#ManyToOne
#JoinColumn(name = "USR_SK")
private User user;
}
#Test
public void testDeleteUserApp() {
List<UserApplication> removedUserApp = userApplicationRepository.findByUserSkAndApplicationSk(1, 5);
userApplicationRepository.delete(removedUserApp);
//*****This is where I see an error that says
//org.springframework.orm.jpa.JpaObjectRetrievalFailureException: deleted entity passed to persist: [UserApplication#<null>]; nested exception is javax.persistence.EntityNotFoundException
userRepository.findByUserLoginName(loginId);
}
I donot know if this will help you but I have something similar and this is what I do to delete the data...
In the repository I have a method like this:-
#Transactional
public Long deleteByByUserSkAndApplicationSk(int userSk, int applicationSk);
The output of the method is the number of rows deleted.
Then you can directly call the method where ever you want to delete.

OpenJPA cascade persist on OneToOne relationship

I'm trying to persist to an IBM DB2 database using Websphere which in turn uses an OpenJPA implementation. My issue is that i'm always getting an integrity constraing exception SQLCODE=-530, meaning it can't find the foreign key.
The relationship is as follows
#Entity
public class A{
#Id
private String id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "B_ID")
private B b;
}
#Entity
public class B{
#Id
private String id;
}
Now i try to persist the two entities.
final A a = new A();
final B b = new B();
a.setB(b);
em.persist(a);
The DB owner of the relationship is the table bound to A containing a reference to B while B has no reference to A.
Now when persisting OpenJPA tries to persist the owner of the #JoinColumn first which results in class B not being there and thus isn't able to persist.
Does anybody have a sollution to this issue maitaining the current relationships.
I've already found this post: http://openjpa.208410.n2.nabble.com/Constraint-violation-using-OneToOne-relationship-td6978223.html
but the sollution proposed there doesn't seem to work.

JPA OneToMany - List doesn't update after add/delete Object, but persist in DB

I have a Problem with my Relationship as follow:
(I'm using JPA not Hibernate!)
I have a 1:N Relationship (Parent:Child).
On a JSP-Site, the Parent and the Children are displayed.
If I load the JSP-Site (FIRST TIME) and Save a Parent with a Child, all is ok, the Parent is saved and the Child is saved too to the DB with the Foreign Key.
After that, a second JSP-Site is displayed with the Parent and Children.
But if I go back to the first Site and save a second Child (ADDING CHILD) to existing Parent, the "childEntityList" is still the old one, i mean, the new Child is not inside this List, but inserted in the DB!
Why the "childEntityList" is old and doesn't update?!
My Codes:
The Parent:
#Entity
#Table(schema = "test", name = "Parent")
#XmlRootElement
#NamedQueries({...})
public class Parent implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "A_SEQ")
#Basic(optional = false)
#NotNull
#Column(name = "ID")
private int id;
#OneToMany(mappedBy = "p", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<ChildEntity> childEntityList = new ArrayList<ChildEntity>();
...
public List<ChildEntity> getChildEntityList() {
return childEntityList;
}
public void setChildEntityList(List<ChildEntity> childEntityList) {
this.childEntityList = childEntityList;
}
public void addChildEntityToParent(ChildEntity c) {
this.childEntityList.add(c);
if(c.getParent() != this) {
c.setParent(this);
}
}
public void removeChildEntityFromParent(ChildEntity c) {
childEntityList.remove(c);
}
...
}
The Child:
#Entity
#Table( name = "Child" )
#NamedQueries({...})
public class ChildEntity implements Serializable {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
#Column( name = "CHILD_ID" )
private Long childId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARENT_ID")
private Parent p;
...
public Parent getParent() {
return p;
}
public void setParent(Parent p) {
this.p= p;
if(!p.getChildEntityList().contains(this)) {
p.getChildEntityList().add(this);
}
}
Save-Code:
Parent p = new Parent(...);
ChildEntity c = new ChildEntity(...);
p.addChildEntityToParent(c);
childEntityFacade.create(c);
View-Code:
...
public doSomething(Parent p) {
super(p);
}
...
List<ChildEntity> cList = p.getChildEntityList();
System.out.print("Size: " + c.size());
...
In the View-Code, the size of the childList is every time the same even I add a Child.
Only when I restart the Server, all Children of the Parent are displayed!
When is the "childEntityList" in the Parent Class filled?
Is it automatically, because of the "#OneToMany"?
Can someone explain it and help me please?!
UPDATE:
After reading some articles, maybe i have a solution.
Should i merge the EntityManager every time when i add/remove a Child Object from the Parent List or have i do some other things?
You need to maintain both sides of a bi-directional relationship.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side
(I'm using JPA not Hibernate!)
I guess you meant that you are not using the Hibernate API.
JPA is only a persistence API specification and, by itself, does nothing.
So, if you're using JPA... you're using something to do the persistence.
JPA delegates the persistence work to a persistence engine such as Hibernate, EclipseLink, OpenJPA or something else.
You can configure your app to use JPA interfaces with a specific persistence provider, or if the app is running in a JEE container, then the JPA implementation should be provided for you: GlassFish (uses OpenJPA), WebLogic (uses EclipseLink), TomEE (uses OpenJPA).
Should i merge the EntityManager every time when i add/remove a Child
Object from the Parent List...
You don't merge the EntityManager; you use it to merge the parent entity:
parent = em.merge(parent);
Relating to your question, when you use merge() in this way... (and because the mapping specifies CascadeType.ALL) then upon reloading page ("Site 1") you should get the refreshed parent object with an updated object network containing changes you made prior to merge(parent), including any newly added children.
Yes, you need to merge parent entity. I was having same issue and it got resolved once I merged parent entity.
Also you need to maintain both side of the relationship.

Merging an object with FetchType.EAGER relation leads to “FailedObject”

I have an entity VM with a relationship to another entity BP. The relationship is eagerly fetched. First I load a VM. After loading the VM is detached, serialized and changed at the client side. Now I want to update the changed entity so I use the EntityManager.merge() method from JPA. Now I run into the following error from OpenJPA:
"Encountered new object in persistent field "Vm.bp" during attach. However, this field does not allow cascade attach. Set the cascade attribute for this field to CascadeType.MERGE or CascadeType.ALL (JPA annotations) or "merge" or "all" (JPA orm.xml). You cannot attach a reference to a new object without cascading."
Why do I have to add a Cascade.MERGE to a relationship to another entity that will never change? And why does JPA think that BP is a new object ("...cannot attach reference to a new object...")?
When using ManyToOne relationships do I always have to add Cascade.MERGE in order to update the entity or is this because of the EAGER fetch type?
Here's my entity:
#Entity
#Table(name = "VM")
public class Vm extends BaseEntity implements Serializable {
public static final long serialVersionUID = -8495541781540291902L;
#Id
#SequenceGenerator(name = "SeqVm", sequenceName = "SEQ_VM")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SeqVm")
#Column(name = "ID")
private Long id;
// lots of other fields and relations
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "BP_ID")
private Bp bp;
// ...
}
I found the reason why this error message comes up: The #Version annotated database field of the related Bp entity was initialized with "0". Apparently OpenJPA (1.2.3) is not able to cope with entity versions of zero.
Setting the version to 1 solved my issue.

Resources