I have an common entity, which has a bidirectional association. The code of the entity is the next one:
#Entity
#Table(name = "TableName")
public class Entity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int ID;
...
#OneToMany(mappedBy = "OtherEntityID", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OtherEntity> OtherEntity;
...
I try to delete the Entity with this code:
Session s = HibernateUtil.getSessionFactory().openSession();
Entity e = s.get(Entity.class, EntityID);
s.beginTransaction();
s.delete(e);
s.getTransaction().commit();
s.close();
However, Hibernate returns this Exception:
Exception in thread "main" org.hibernate.AssertionFailure: collection owner not associated with session
If I set orphanRemoval=false, then I'm able to remove the entity without any problem. I've tried initializing the Collection before deleting the entity with Hibernate.initialize(), but nothing changed.
I suppose this has something to do with the fact that the Collection is loaded LAZY and not EAGGER, but I've not found any solution yet. Could anybody help me?
PD: I'm using Hibernate 5.2.4 Final.
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.
We are using Spring Data repositories with Hibernate 5.x
We have a entity graph with a deep hierarchy.
The mapping looks like this:
#Entity
public class FooBar {
#OneToMany(mappedBy = "fooBar", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
...
}
#Entity
public class Foo {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "foobar_id")
private FooBar fooBar;
#OneToMany(mappedBy = "foo", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Bar> chassis = new HashSet<>(0);
...
}
#Entity
public class Bar {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "foo_id")
private FooBar foo;
...
}
As you can see the FooBar entity has a set of Foo entities. Each Foo entity contains more Bar entities and so on.
We use the Fetchgraph feature to load the FooBar entity with the relations we need during runtime to avoid n+1 query issue when fetching lazy associations.
After the service call to load the entity graph the transaction has ended and the entity is detached.
When calling save on the FooBar entity at a later time, this causes multiple select statements. Each fetching one of the child entities.
I know that this comes from the entitymanager merge() call which fetches the object graph from the db before copying state changes from the detached objects.
I have two questions:
Why is hibernate not able to join these statements to one big select like what happens when using the fetchgraph?
When i remove all cascade options from the relations it still causes multiple selects but only attributes of the top, FooBar entity, will be updated. Why is hibernate still fetching all loaded child entites during merge even with no cascade merge?
Thanks
You can use session.update instead of merge to overcome this issue.
Session session = entityManager.unwrap(Session.class);
for (Post post: posts) {
session.update(post);
}
I have similar issue with your case, and the reason is the setting of cascading CascadeType.ALL on the #OneToMany association. Updating and merging the parent entity cause a lot of select on the child association.
#Entity
public class FooBar {
#OneToMany(mappedBy = "fooBar", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
...
}
I fix my case by reducing the scope of cascading, only PERSIST and REMOVE is sufficient
#OneToMany(mappedBy = "fooBar", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
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
}
I have a class Customer that has a OneToOne bidirectional relationship with a Subscription:
#Entity
#Table(name = "customers")
public class Customer{
#OneToOne(mappedBy="customer",cascade = CascadeType.ALL)
private Subscription currentSubscription;
}
#Entity
#Table(name = "subscriptions")
public class Subscription {
#Id
#Column(columnDefinition = "INT8",name="id", unique=true, nullable=false)
#GeneratedValue(generator="gen")
#GenericGenerator(name="gen", strategy="foreign", parameters=#Parameter(name="property", value="customer"))
private Long id;
#OneToOne
#PrimaryKeyJoinColumn
private Customer customer;
}
Now, when I create a customer with a subscription and call persist on the customer, it nicely saves the subscription as well into the database. However when I have already persisted a customer, and want to add a subscription, it fails with the following error:
Caused by: org.hibernate.id.IdentifierGenerationException: attempted
to assign id from null one-to-one property
[com.qmino.miredot.portal.domain.Subscription.customer]
I've written a test in order to explain what I want to achieve:
#Test
public void shouldCascadeUpdateSubscription(){
Customer owner = customerRepository.save(CustomerMother.getCustomer(false));
Subscription subscription = SubscriptionBuilder.create()
.setBillingDayOfMonth(LocalDate.now().getDayOfMonth())
.setSubscriptionPlan(subscriptionPlan)
.build();
subscription.setCustomer(owner);
owner.setCurrentSubscription(subscription);
customerRepository.save(owner);
Customer result = customerRepository.findOne(owner.getId());
assertThat(result.getCurrentSubscription(),is(notNullValue()));
assertThat(result.getCurrentSubscription().getId(),is(result.getId()));
}
Where did I go wrong?
Cascade here is not the problem, Cascade indicates the action to be done by entity when deleted or updated. What is correct if you want to save complete entity. But for that, you need to have the correct data, your message suggest it tries to update the Customer entity but it founds an empty AccountDetails, so in order to correctly fetch the other entities, you need to add FecthType.EAGER, to get all attributes of mapped entities.
#OneToOne(mappedBy="customer",cascade = CascadeType.ALL, fetch = FetchType.EAGER))
I have two classes which have a Unidirectional One to Many relation with each other.
public class Offer{
...
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name = "Offer_Fields",
joinColumns =
#JoinColumn(name = "OFFER_ID"),
inverseJoinColumns =
#JoinColumn(name = "FIELDMAPPER_ID"))
private Set<FieldMapper> fields = new HashSet<FieldMapper>();
}
#Entity
#Table(name = "FieldMapper")
public class FieldMapper implements Serializable {
#Id
#Column(name = "FIELDMAPPER_ID")
#GeneratedValue
private int id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "multilingual_field_fk")
private MultiLingual field;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "multilingual_value_fk")
private MultiLingual value;
}
I want to store an Offer with a set of FieldMapper to database.
When I Use CascadeType.ALL in my OneToMany, I got this error:
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
and when I change CascadeType to something else I got this error:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.RCSTT.library.FieldMapper
and here is where I save Offer :
public void insert(Offer offer) throws SQLException {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
session.save(offer);
tx.commit();
session.close();
}
and I don't use session in somewhere else.
in tx.commit(); line throws explained exceptions.
Thanks for your help.
The first exception leads me to believe that this is not a problem with your mapping/entities but with how the object lifecycle or Hibernate session is handled.
Are the FieldMappers that you're trying to save already persistent (in another session)? You might need to detach those first.