JPA potential Sync Issue - jpa

In my lodger class, i have
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "lodger", orphanRemoval=true)
// #JsonBackReference
private List<RoomPayment> roomPaymentList;
In my RoomPayment class i have
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lodger_id")
private Lodger lodger;
Actually i do
#Override
public Long save(RoomPaymentDto roomPaymentDto) {
RoomPayment roomPayment;
if (roomPaymentDto.getRoomPaymentId() != null) {
roomPayment = repository.findOne(roomPaymentDto.getRoomPaymentId());
} else {
roomPayment = new RoomPayment();
}
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(RoomPayment.class, RoomPaymentDto.class).byDefault().register();
MapperFacade mapper = mapperFactory.getMapperFacade();
mapper.map(roomPaymentDto, roomPayment);
repository.save(roomPayment);
return roomPayment.getRoomPayementId();
}
In this method, do i need to get the Lodger and assign it to roomPayment?

No, if the dto contains the right Lodger of course.
The point is that there are no cascades from RoomPayment to the associated Lodger, meaning that Hibernate will not complain about the detached object when you save the roomPayment.
Hibernate will just take the id from the lodger instance which it will use to maintain the association (because RoomPayment is the owner of the association).

Related

JPA Mapping OneToMany with partial embedded id child

Simple example (hopefully). I have a primary key (using a sequence) in one table and that value is a partial FK into a child table. I see the Parent is trying to be saved with a generated sequence, but then I see an exception that the parentId in the embeddable is null while saving the child. The sequence value used for the parent is not being carried over to the child. I have tried many annotations and mappedBy/join column names but no luck.
Any pointers would be very much appreciated.
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "audit_seq")
#SequenceGenerator(name = "audit_seq", allocationSize = 5)
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent")
private List<Child> childList = new ArrayList<>();
//Used to add child record o the parent
public void addChild(Child child) {
this.childList .add(child);
child.setParent(this);
}
}
#Embeddable
public class ChildId {
private Long parentId;
private String name;
}
public class Child {
#EmbeddedId
private ChildId id;
private String myCol;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "parentId", insertable = false, updatable = false)
private Parent parent;
}
I was able to get this resolved with the use of a couple of annotations:
Parent class:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent", orphanRemoval = true)
#PrimaryKeyJoinColumn
private List<Child> childList = new ArrayList<>();
Child class:
#ManyToOne
#MapsId("id")
#JoinColumn(name = "id")
private Parent parent;
Now all objects are being persisted when saving the parent with the appropiate sequence id.

detached entity passed to persist: A class with ManyToOne relation to 2 more classes

Comment class
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "image_id")
private Image image;
User class
#OneToMany(mappedBy = "user", cascade = CascadeType.MERGE, fetch =
FetchType.EAGER)
private List<Comment> comments = new ArrayList<>();
Image class
#OneToMany(mappedBy = "image",cascade = CascadeType.MERGE,fetch =
FetchType.EAGER)
private List<Comment> comments = new ArrayList<Comment>();
These are the ManyToOne and OneToMany relations I have. I am unable to persist the Comment object due to "detached entity passed to persist: ImageHoster.model.Comment" error.
I kind of figured out the issue. In the CommentRepository class where I am persisting the Comment object, I used .merge instead of .persist
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
try{
transaction.begin();
em.merge(newComment);
instead of em.persist(newComment);

#ManyToOne Lazy loading not working

I've seen other posts about this problem, but have found no answer to my own troubles. I have
#Entity
#Table(name= ServerSpringConstants.COMPANY)
public class Company implements Serializable {
private static final long serialVersionUID = -9104996853272739161L;
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column (name = "companyID")
private long companyID;
#OneToMany (targetEntity = Division.class, cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REFRESH},
fetch = FetchType.EAGER)
#JoinTable (name = "companyDivisionJoinTable",
joinColumns = #JoinColumn(name="companyID"),
inverseJoinColumns = #JoinColumn(name="divisionID")
)
private Set<Division> divisions = new HashSet<>();
public long getCompanyID() {
return companyID;
}
public Set<Division> getDivisions() {
return divisions;
}
public void setDivisions(Set<Division> divisions) {
this.divisions = divisions;
}
}
On the other side:
#Entity
#Table(name= ServerSpringConstants.DIVISION)
public class Division implements Serializable {
private static final long serialVersionUID = -3685914604737207530L;
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
#Column(name = "divisionID")
private long divisionID;
#ManyToOne
(fetch = FetchType.LAZY, optional = false, targetEntity = Company.class,
cascade = {CascadeType.PERSIST, CascadeType.MERGE
}
)
#JoinColumn(name="companyID", referencedColumnName = "companyID")
private Company company;
public long getDivisionID() {
return divisionID;
}
public void setDivisionID(long divisionID) {
this.divisionID = divisionID;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
Yet for some reason, LAZY loading not working. I'm using JPA. I'm calling back the companies, and their enclosing divisions from within a 'User' class -- the pertinent part
#ManyToMany (targetEntity = Company.class,
cascade={
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REFRESH},
fetch=FetchType.EAGER )
#JoinTable (
name="companyUserJoinTable",
joinColumns=#JoinColumn(name="userID"),
inverseJoinColumns=#JoinColumn(name="companyID")
)
private Set<Company> company = new HashSet<>();
I've searched out existing threads, and have tried adding various suggestions, but nothing has helped!
Any help appreciated.
Thanks!
Since you are loading the divisions set eagerly (with fetch = FetchType.EAGER) and you have a bidirectional association, divisions will be initialized with the parent reference to company. I can't see any problem with it. Jpa loaded the full object tree because you just told it so. A company contains divisions which contain a back reference to the company that loaded them.
To understand it better, since the reason for lazy loading is to reduce the data loaded from database, the owning company is already loaded in session for the divisions, so why not setting the association too?
The #ManyToOne association on the other side takes effect if you load divisions first.
To be more correct with your mapping add also a #MappedBy attribute to the one part of the association. This does not affect fetching behavior but will prevent double updates to the database issued by both ends of the association.

Updating multiple entities with jpa 2

I need help to understand how ORM works. Here is the scenario that is very common. I have two main tables Organization and RelatedParty which have to be in many to many relation. But there is also relation_type attribute that defines what kind of relation exists between Organization and Relatedparty.
Here are my entity classes:
Organization:
#Entity
#Table(name = "organization", catalog = "...", schema = "")
#XmlRootElement
public class Organization implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "organization_id", nullable = false)
private Integer organizationId;
#Column(name = "organization_name", nullable = false)
private String organizationName;
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "organization")
private List<Organdrelatedparty> organdrelatedpartyList;
...
//getter setter methods
Organdrelatedparty: which uses composite primary key OrgandrelatedpartyPK
#Entity
#Table(name = "organdrelatedparty", catalog = "...", schema = "")
#XmlRootElement
public class Organdrelatedparty implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected OrgandrelatedpartyPK organdrelatedpartyPK;
#JoinColumn(name = "relatedParty_id", referencedColumnName = "relatedParty_id", nullable = false, insertable = false, updatable = false)
#ManyToOne(optional = false, cascade= {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
private Relatedparty relatedparty;
#JoinColumn(name = "orgRelation_id", referencedColumnName = "orgRelation_id", nullable = false)
#ManyToOne(optional = false)
private ParOrgrelationtype orgRelationid;
#JoinColumn(name = "organization_id", referencedColumnName = "organization_id", nullable = false, insertable = false, updatable = false)
#ManyToOne(optional = false)
private Organization organization;
...
//getter setter methods
OrgandrelatedpartyPK
#Embeddable
public class OrgandrelatedpartyPK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "relatedParty_id", nullable = false)
private int relatedPartyid;
#Basic(optional = false)
#NotNull
#Column(name = "organization_id", nullable = false)
private int organizationId;
...
//getter setter methods
RelatedParty: which is in unidirectional oneToMany relationship with organdRelatedParty class. In other word that relatedParty entity has no knowledge about organdRelatedParty entity that is on the other side.
#Entity
#Table(name = "relatedparty", catalog = "...", schema = "")
#XmlRootElement
public class Relatedparty implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "relatedParty_id", nullable = false)
private Integer relatedPartyid;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(name = "firstName", nullable = false, length = 100)
private String firstName;
#Size(max = 100)
#Column(name = "lastName", length = 100)
private String lastName;
#Basic(optional = false)
#NotNull
#Column(name = "isForeign", nullable = false)
private boolean isForeign;
...
//getter setter methods
For insertion, if I persist new Organization Entity, it cascades persist activity to new OrgandrelatedParty which also cascades persist activity to new RelatedParty. So all the related entities are persisted and it works fine.
For updating, User is expected to change existing organization and relatedParty entities and also add new relatedParty to organization. So we prefer to delete all OrgandrelatedParties first and add new relatedParties and edited relatedParties again after that.
This is our method that handles updating: We pass new organization and also all new and old relatedParties as a list to method:
firs we delete all old OrgAndRelatedParties then we create again all relatedParties in list as new OrgandrelatedParties. This is main method to update organization.
public void updateOrganization(Organization newOrganization, List<Relatedparty> newShareList) throws ControlException {
try{
tx.begin();
this.updateOrgAndRelatedShares(newOrganization, newShareList);
customerController.updateOrganization(newOrganization);
tx.commit();
}catch(ControlException ex){
...
customerController's updateOrganization method does first find old Organization by find method of entity manager then copies all attributes of new organization to old then merges old organization and flush:
public void updateOrganization(Organization newOrganization)
{
Organization preOrganization = em.find(Organization.class, newOrganization.getOrganizationId);
preOrganization.setOrganizationId(newOrganization.getOrganizationId);
preOrganization.setOrganizationName(newOrganization.getOrganizationName);
em.merge(preOrganization);
em.flush();
}
here are other methods:
#TransactionAttribute(TransactionAttributeType.REQUIRED)
private void updateOrgAndRelatedShares(Organization org, List<Relatedparty> shareList) throws ControlException
{
for(Iterator<Organdrelatedparty> it = org.getOrgandrelatedpartyList().iterator(); it.hasNext();)
{
Organdrelatedparty op = it.next();
it.remove();
op.setOrganization(null);
op.setRelatedparty(null);
deleteOrgRelated(op);
}
org.getOrgandrelatedpartyList().clear();
for(Relatedparty relatedParty: shareList){
int parOrgRelationTypeId = relatedParty.getIsPerson() ? 1:2;
createOrgAndRelatedParty(org, relatedParty, parOrgRelationTypeId);
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteOrgRelated(Organdrelatedparty org) throws ControlException{
try{
org = em.find(Organdrelatedparty.class, org.getOrgandrelatedpartyPK());
em.remove(org);
em.flush();
}
catch(Exception ex){
Logger.getLogger(RelatedpartyController.class.getName()).log(Level.SEVERE, null, ex);
throw new ControlException("Couln't delete org relation", ex);
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
private void createOrgAndRelatedParty(Organization org, Relatedparty relatedParty, int parOrgRelationTypeId) throws ControlException{
if(findRelatedPartyByRegNum(relatedParty.getRegisterNumber()) == null || relatedParty.getRelatedPartyid() == null){
createRelated(relatedParty);
}else{
relatedParty = updateRelatedParty(relatedParty);
}
Organdrelatedparty preOrp = new Organdrelatedparty(relatedParty.getRelatedPartyid(),
preOrp.setOrganization(org);
preOrp.setRelatedparty(relatedParty);
preOrp.setOrgRelationid(prepareOrgandRelatedPartyType(parOrgRelationTypeId));
org.getOrgandrelatedpartyList().add(preOrp);
}
And my question is when I merge organization entity with new List organdrelatedpartyList
it throws exception like this:
SEVERE: java.lang.IllegalArgumentException: Cannot merge an entity that has been removed: mn.bsoft.crasmonclient.model.Organdrelatedparty[ organdrelatedpartyPK=mn.bsoft.crasmonclient.model.OrgandrelatedpartyPK[ relatedPartyid=71, organizationId=19 ] ]
I found out that eclipseLink does persist operation first then remove operations. So I think that it tries to insert organdrelatedparty entity that has same composite id with entity which was not deleted previously from database. I flushes every time I remove old organdrelatedparties. But it doesn't help. What is the solution? Any idea guys.
I'm using jpa 2.0; eclipseLink as provider and glassfish 3.1.2
You seem to be making these a lot more complicated than they need to be.
Why don't you just remove the Organdrelatedparty that have been removed, instead of deleting all of them, then reincarnating some of them? Reincarnating objects, especially in the same transaction is normally a bad idea.
The error that is occurring is on merge() according to the code you included you are only call merge in updateOrgAndRelatedShares(), so I don't see how this object is removed at this point? Or is your code different than you show, please include the exception stack.
You updateOrganization() method is bad, it updates the objects Id, which you should never do. Also it calls merge for no reason, it already changed the object.
Also I would normally recommend using an IdClass instead of an EmbeddedId, and recommend using TABLE or SEQUENCE id generation instead if IDENTITY.

Entity Object Not Getting Persisted: New object was found through a relationship that was not marked

I am trying to persist the following entity:
public class CommentEntity {
#Id
#ManyToOne(optional=false,cascade=CascadeType.REFRESH, targetEntity=UserEntity.class)
#JoinColumn(name="USERID",referencedColumnName="USERID")
private UserEntity userEntity;
#ManyToOne(optional=false,cascade=CascadeType.REFRESH, targetEntity=PostEntity.class)
#JoinColumn(name="POSTID",referencedColumnName="POSTID")
private PostEntity postEntity;
}
But the error comes out:
During synchronization a new object was found through a relationship that was not marked cascade PERSIST: entity.PostEntity#16afec.
when I try to persist PostEntity, it is persisted. No such exception is thrown:
public class PostEntity {
#ManyToOne(optional = false, cascade = CascadeType.REFRESH, fetch = FetchType.LAZY, targetEntity = UserEntity.class)
#JoinColumn(name = "USERID", referencedColumnName = "USERID")
private UserEntity userEntity;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "postEntity", fetch = FetchType.LAZY)
private List<CommentEntity> commentEntityList;
}
Why it is happening? UserEntity is:
public class UserEntity {
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "userEntity")
private List<PostEntity> postEntityList;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "userEntity")
private List<CommentEntity> commentEntityList;
}
I am persisting commentEntity using the code:
entityManagerFactory = Persistence
.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
entityManager = entityManagerFactory.createEntityManager();
entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
entityManager.persist(commentEntity);
entityTransaction.commit();
entityManager.close();
I am not able to understand what can be the cause? And then why PostEntity is getting persisted in the same situation?
I am using EclipseLink
Because you try to persist CommentEntity that have either userEntity or postEntity (or both) referencing to the entity that is not persisted. Call to em.persist(instance of commentEntity) does not cause those entities to be persisted, because you cascaded only refresh. You should persist those entities with separate calls to em.persist.
If this does not answer to your question, please provide the code that creates entities and persists them.