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
}
Related
I try to cascade-persist the following two JPA entities (getters/setters and other fields ommited):
#Entity
public class TestEntity {
#Id #GeneratedValue
private Integer id;
private String name; // field only necessary so there is at least one mapping in the table
#ElementCollection
#MapKeyJoinColumn(name="PROPERTY_KEY", referencedColumnName="ID")
#Column(name="PROPERTY_VALUE")
#CollectionTable(name="PROPERTIES")
private Map<TestKey, String> properties = new HashMap<>();
}
#Entity
public class TestKey {
#Id #GeneratedValue
private Integer id;
private String name;
}
This is the code I use to persist the entity:
EntityManager em = ...
em.getTransaction().begin();
TestKey key = new TestKey();
key.setName("some key");
TestEntity entity = new TestEntity();
entity.getProperties().put(key, "some value");
em.persist(entity);
em.getTransaction().commit();
em.close();
But when I do this, I get the following exception:
java.lang.IllegalStateException: During synchronization a new object was found through a relationship
that was not marked cascade PERSIST: TestKey
I am able to fix this by persisting the key before persisting the entity like so:
[...]
em.persist(key); // <<-- line added to previous example
em.persist(entity);
em.getTransaction().commit();
em.close();
This code works as expected and persists both the TestEntity and the TestKey.
Is it somehow possible to cascade-persist the entity without explicitly persisting the key - am I perhaps doing something wrong here?
I actually thought that all #ElementCollection mappings are automatically cascade-persisted. I came to this conclusion from the follwing statements:
https://wiki.eclipse.org/EclipseLink/Examples/JPA/2.0/ElementCollections
Their is no cascade option on an ElementCollection, the target objects
are always persisted, merged, removed with their parent.
https://stackoverflow.com/a/19517505/3270595:
You don't need #ManyToMany annotation here. Operations on
ElementCollections are always cascaded.
I'm using EclipsLink version 2.5.2.v20140319-9ad6abd 2.7.5.v20191016-ea124dd158.
I'm getting this error when I try to persist an A class Object:
Detail: Key (classB)=() is not present in table "b".
I need to have the possibility to insert the object with null on the referenced column.
The problem is hibernate convert the null value on an empty string, so when I try to persist the object, it fails.
If I put cascade = CascadeType.ALL on #ManyToOne works, but it creates a row on B table with ID = 0 and an empty string as refColName value. I want to avoid this because de A class is the child, and the cascade should be in B class.
#Entity
#Table
public class A {
...
#ManyToOne
#JoinColumn(name = "class_b", referencedColumnName = "refColName", nullable = true)
private B classB;
...
}
#Entity
#Table
public class B {
...
#Id
#Column (name = "id")
private long id;
#Column(name = "refColName")
private String refColName;
...
}
Any suggestion?
Thanks for your time
Edit:
It's a unidirectional relationship, where B is a master data table, so i have predefined values. refColName should be a String. I use referencedColumnName because I canĀ“t take the id as a foreign key.
I need to have the possibility to insert the object with null on the
referenced column.
Please make sure the classA input to persist should be like below(representing in json)
{
classB:null
}
and not
{
classB:{
refColName:null
}
}
Lately I encountered a strange phenomenon with my Hibernate/Postgres data. On unknown conditions Hibernate creates orphans of child entities (null foreign key field to parent). But I have annotated the #OneToMany relation as orphanRemoval.
The "main" entity:
#Entity
#Audited
public class Product {
#NotNull(groups = { CheckId.class })
#NotBlank(groups = { CheckId.class })
#Id
#Column(length = 32)
private String id = IdGenerator.createId();
#Version
private Integer version;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval=true)
#JoinColumn(name=Product.PRODUCT_ID)
#Index(name="idx_prod_ident")
private Set<Identifier> identifiers;
...
}
One child entity:
#Entity
#Audited
public class Identifier {
#NotNull(groups = { CheckId.class })
#NotBlank(groups = { CheckId.class })
#Id
#Column(length = 32)
private String id = IdGenerator.createId();
#Version
private Integer version;
#NotNull
#Column(nullable=false, length=3)
#Index(name = "idx_id_prod_type")
private String type;
#NotNull
#Column(nullable=false, length=65)
#Index(name = "idx_id_prod_value")
private String value;
...
}
There are lots of such #OneToMany relations with different (but structurally similar) entities in Product. Millions of records are written correctly, but in most of them I occasionally encounter some hundreds with product_id null. How is this possible?
Unfortunately I cannot easily determine when this happens (due to the missing product_id). Also product_id is not part of the Envers history table of the child entities. So I cannot examine if the product still exists, and what the services have done with it lately.
For information: when a child entity is removed from the parent, this is done via
product.getIdentifiers.remove(identifier);
or
product.getIdentifiers.removeAll(identifiers);
or
product.getIdentifiers.clear();
This is hopefully a valid way to remove them ;)
If I correctly understand you use FetchType.LAZY - it mean the data will be retrieved only during the session, you can use Hibernate.initialize for retrieving #OneToMany fields or can use FetchType.EAGER instead FetchType.LAZY
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?
I've read the documentation and thought I'd be able to do the following....
map my classes as so (which does work)
#Entity
public class ParentEntity
{
...
#OneToMany(mappedBy = "parent")
private List<ChildEntity> children;
...
}
#Entity
public class ChildEntity
{
...
#Id
#Column
private Long id;
...
#ManyToOne
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumn(name = "parent_id")
private ParentEntity parent;
...
}
.. but i want to be able to insert into both tables in one go and thought this would work:
parent = new ParentEntity();
parent.setChildren(new ArrayList<ChildEntity>());
ChildEntity child = new ChildEntity();
child.setParent(parent);
parent.getChildren().add(child);
session.persist(parent);
Can anyone tell me what i'm missing?
Do i need to save the parent first, then add the child and save it again?
thanks.
You have to add #OneToMany(cascade=CascadeType.PERSIST). You can also have CascadeType.ALL which includes persist, merge, delete...
Cascading is the setting that tells hibernate what to do with collection elements when the owning entity is persisted/merged/deleted.
By default it does nothing with them. If the respective cascade type is set, it invokes the same operation for the collection elements that were invoked for the parent.