Are JPA entity relationships reciprocated? - java

I've always wondered if JPA entity relationships are reciprocate or not. Let's take #OneToOne Java 7 tutorial says: "One-to-one: Each entity instance is related to a single instance of another entity" Java 7 tutorial.
Does this mean that each instance of the other entity is related with one instance of this entity? Not clear for me.
But Microsoft in Types of table relationships say : "In a one-to-one relationship, a row in table A can have no more than one matching row in table B, and vice versa. A one-to-one relationship is created if both of the related columns are primary keys or have unique constraints." Very clear, but does this apply to JPA(standard)/Hibernate(impl)? Or is is that each company assigns the meaning that they want to the relationships?
#Entity
public class ParentEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String parent;
#OneToOne(cascade = CascadeType.ALL)
private ChildEntity childEntity;
And the child entity (NOTE: Unidirectional relationship):
#Entity
public class ChildEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String child;
And a test which passes, note that both parent entities are related to the same child:
#Test
public void canCreateAndRetrieve() throws NotSupportedException, SystemException, DaoException, SecurityException, IllegalStateException, RollbackException, HeuristicMixedException, HeuristicRollbackException {
//given
ParentEntity pe1 = new ParentEntity();
pe1.setParent("Parent1");
ParentEntity pe2 = new ParentEntity();
pe2.setParent("parent2");
ChildEntity childEntity1 = new ChildEntity();
childEntity1.setChild("Child1");
pe1.setChildEntity(childEntity1);
pe2.setChildEntity(childEntity1);
utx.begin();
try {
//when
parentDao.create(pe1);
parentDao.create(pe2);
} finally {
utx.commit();
}
utx.begin();
try {
List<ParentEntity> results = parentDao.getAll();
//then
Assert.assertEquals(2, results.size());
ParentEntity dbParentEntity1 = results.get(0);
ParentEntity dbParentEntity2 = results.get(1);
assertEquals(dbParentEntity1.getChildEntity().getChild(), dbParentEntity2.getChildEntity().getChild());
} finally {
utx.rollback();
}
}
If I modify the childEntity to make the #OneToOne bidirectional, the test fails:
#Entity
public class ChildEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String child;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "childEntity")
private ParentEntity parentEntity;
Does this mean that in Hibernate relationships are not reciprocate unless they are bidirectional? And that Hibernate allows you to have some sort of hybrid/partial relationships => the unidirectional relationships? Do you guys have some documentations I could read?

Related

Duplicate entry when using one to one relationship with shared primary key in JPA

I followed the example of Modeling With a Shared Primary Key as below:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
//...
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address address;
//... getters and setters
}
#Entity
#Table(name = "address")
public class Address {
#Id
#Column(name = "user_id")
private Long id;
//...
#OneToOne
#MapsId
#JoinColumn(name = "user_id")
private User user;
//... getters and setters
}
However, if there are already a record with id 123456 in address table, then I tried to update the record like below:
Address po = new Address();
po.setId(123456L);
po.setCountry("TW");
AddressRepository.save(po);
Duplicate entry '123456' for key Exception will occur. Why JPA will insert a new record instead of merging it? How to solve this problem?
I know the reason finally. It is because the entity has version field and the version field in the new entity is null.
We need to dig into the source of of save() method in JPA.
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
Then, if we don't override the isNew(), it will use the default isNew() of JpaMetamodelEntityInformation.
#Override
public boolean isNew(T entity) {
if (!versionAttribute.isPresent()
|| versionAttribute.map(Attribute::getJavaType).map(Class::isPrimitive).orElse(false)) {
return super.isNew(entity);
}
BeanWrapper wrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
return versionAttribute.map(it -> wrapper.getPropertyValue(it.getName()) == null).orElse(true);
}
Here, we can see that if version is present and the version is different from the existing record in the database, the entity will be a new entity and JPA will execute the insert action. Then, it will occur the error of duplicate entry.

JPA/Hibernate: How to persist duplicate values in same session for field having #Id annotation?

Customer said no Primary Key Required in Child Table. So Child table has two column "ID" and "Value" where ID can be duplicated.
When i remove #Id then hibernate says "No identifier specified for entity"
When i keep #Id in code then hibernate says "javax.persistence.EntityExistsException: a different object with the same identifier value was already associated with the session" ; while persisting
So crux is that i need to keep #Id but how to persist two same ID in one session with #Id annotation.
Following is code:
Main Entity:
public class CustomerAgreement implements Serializable {
#OneToMany(mappedBy = "customerAgreement", orphanRemoval = true, fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST})
private List<CustomerAgreementComputerAttachments> autoAttachComputersFromOrganizations;
Composed Entity:
public class CustomerAgreementComputerAttachments implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name = "ID")
private CustomerAgreement customerAgreement;
Main Program:
public static List<CustomerAgreement> create() {
List<CustomerAgreement> li = new ArrayList<CustomerAgreement>();
CustomerAgreement cAgreement = new CustomerAgreement();
cAgreement.setId(2222l);
cAgreement.setName("Tillu");;
cAgreement.setCustomerId("140");
List<CustomerAgreementComputerAttachments> catl = new ArrayList<>();
CustomerAgreementComputerAttachments catt = new CustomerAgreementComputerAttachments();
catt.setAttachmentValue("TEST");
catt.setCustomerAgreement(cAgreement);
CustomerAgreementComputerAttachments tatt = new CustomerAgreementComputerAttachments();
tatt.setAttachmentValue("TESTy");
tatt.setCustomerAgreement(cAgreement);
catl.add(catt);
catl.add(tatt);
cAgreement.setAutoAttachComputersFromOrganizations(catl);
li.add(cAgreement);
return li;
}
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("IntegratorMasterdataPU");
em = emf.createEntityManager();
em.getTransaction().begin();
for(CustomerAgreement ca: create()) {
em.persist(ca);
}
em.getTransaction().commit();
}
An Entity must be identifiable by a unique key. This doesn't need to correspond to any database primary key but there must a unique column or columns that can be used to identity an entity.
If this is not possible, then you need to make CustomerAgreementComputerAttachment an #Embeddable.
An #Embeddable unlike an entity has no independent identity (no #ID). See further here:
What is difference between #Entity and #embeddable
#Entity
public class CustomerAgreement {
#ElementCollection
#JoinTable(name="...", joinColumn = "id")
private List<CustomerAgreementComputerAttachment> attachments;
}
and
#Embeddable
public class CustomerAgreementComputerAttachments {
//No back reference to CustomerAgreement
//Other fields as required.
}

Cannot delete a row in table using JPA without throwing Exception

I have some code that deletes a row from a table using the following code:
EntityManager em = getEntityManager();
try
{
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.remove(data);
transaction.commit();
} catch (final PersistenceException e) {
{
throw new CPDPersistenceException(e);
}
The error message is: Cannot delete or update a parent row: a foreign key constraint fails.....etc.
The problem is that a foreign key for other tables "reference_id" exists in the table that I am trying to delete. However, wherever this primary key exists in the persistent Java object where it is defined, it has a reference that should cause cascading deletion. For example:
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="reference_id")
private Reference reference;
My understanding, from reading other entries on this subject, is that the "cascade" phrase attached to reference would fix the problem of deleting one entry that is related to other entries in other tables. Does anybody have any ideas?
What I understand from your code, you shouldn't set cascade property of #ManyToOne annotation for the owning entity's field of the referenced entity. On the contrary, you must set the cascade property of #OneToMany annotation in the parent entity for the child entity field. For example:
class ParentEntity {
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private ChildEntity child;
...
}
class ChildEntity {
#ManyToOne
private ParentEntity parent;
...
}
You are using unidirectional hibernate relationship. And because you are using cascade property for the reference field of both Uuid and Attachment entities, just manipulations on these two entities affect Reference entity, not vice versa.
I recommend using bidirectional relationship and set the cascade property of the #OneToMany annotation for your both uuid and attachment fields in your Reference entity to get the desired result, as follows:
public class Reference implements Serializable {
#Id
#Column(name = "reference_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int referenceID;
#OneToMany(mappedBy = "reference", cascade=CascadeType.ALL)
private Attachment attachment;
#OneToMany(mappedBy = "reference", cascade=CascadeType.ALL)
private Uuid uuid;
MORE STUFF
}
public class Attachment implements Serializable {
#ManyToOne
#JoinColumn(name = "reference_id")
private Reference reference;
MORE STUFF
}
public class Uuid extends Serializable {
#ManyToOne
#JoinColumn(name = "reference_id")
private Reference reference;
MORE STUFF
}

JPA: persisting entity with composite primary key

I have two tables in database, A and B. Table B has an id composed of two fields. One of them is a foreign key to A. Id of A is automatically generated on insert by a sequence.
A:
ID (PK)
(*other fields*)
B:
SOME_FIELD (PK)
A_ID (PK, FK to A)
I have mapped the two tables in JPA (Hibernate) following JPA specification, in this way:
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "A_SEQ", sequenceName = "A_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "A_SEQ")
#Column(name = "ID")
private Long id;
(...)
}
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
#AttributeOverride(name = "someField", column = #Column(name = SOME_FIELD))
private BPK pk;
#MapsId("aId")
#ManyToOne
#JoinColumn(name = "A_ID")
private A a;
(...)
}
#Embeddable
public class BPK implements Serializable {
private Long aId;
private String someField;
#Override
public boolean equals(Object o) {
(...)
}
#Override
public boolean hashCode() {
(...)
}
(...)
}
The problem is that when I try to save an B object calling entityManager.persist(b), where b.a is set to an A object, which exists already in database, I get an exception:
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: package.name.A
I don't know why this happens. I'm trying to save object of class B, not A. Is my entity class wrong? Or maybe I shouldn't use persist here?
It could be that the entity A is no longer being held by entity manager. Have you tried setting B.a with a "fresh" instance of A?
b.setA(get(b.a));
entityManager.persist(b);
The get(b.a) method can be whatever you usually use to find entity A from your datasource e.g. entityManager.getReference(A.class, a.id);

Hibernate: Is it possible to persist only a child which will cause persisting a parent as well

Let's say I have a parent table A and a child table B, they have a foreign key linking A.id and B.id, the problem is this parent table and the FK have been created recently and there are too many places which persisting a child table, so I'm waiting for a simple way to persist the parent table and link it to the child table. Is it possible to handle this task by using Hibernate instruments? I was trying to use cascades but no success, looks they only work if we persist a parent entity.
So the structure is the following:
#Entity
class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "a_id")
long id;
}
#Entity
class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "b_id")
long id; // FK to a_id
}
class BDao {
void store(B b) {
session.save(b); // to many places
}
}
Will appriciate any help, thanks!
Yeah! I've fixed it, just in case somebody wants to know the answer I'm leaving my solution here. Perhaps you guys will spent less time than me solving the same problem.
So first you need to use #Inheritance(strategy= InheritanceType.JOINED) annotation to describe the parent entity:
#Entity
#Inheritance(strategy= InheritanceType.JOINED)
#PrimaryKeyJoinColumn(name = "a_id")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "a_id")
protected Integer id;
}
Then describe the child entity like
#Entity
#AssociationOverride(name="a_id", joinColumns = #JoinColumn(name="b_id"))
#PrimaryKeyJoinColumn(name = "b_id")
public class B extends A {
...
}
I've left here #AssociationOverride because in my case I have different primary key name so perhaps it helps somebody as well.
That's it! Now when you persist a child entity it will create two rows - one for a parent and one for a child tables.

Categories

Resources