jpa cascading deletes reverse relationship - java

My question is about cascading deletes with JPA and Eclipselink.
I would like to model a simple relationship between two entities: A and B. B references A through a property ref2a (in DB terms B.ref2a is connected to A.id through a foreign key with "ON DELETE CASCADE").
My goal is when an A object is deleted to cascade the delete to all B objects that reference it.
I searched a lot, but I cannot make it work. Most solutions I have found are for the opposite situation: A contains a collection of references to B. This works like a charm. But if the reference is on the B side, I don't know how to do it.
Here is the Code sample:
#Entity
public class A
{
#Id
#GeneratedValue
private Integer id;
private String name;
// ...
}
#Entity
public class B
{
#Id
#GeneratedValue
private Integer id;
private String name;
#OneToOne
#JoinColumn(
foreignKey=#ForeignKey(
foreignKeyDefinition="FOREIGN KEY ref2a REFERENCES A id ON DELETE CASCADE"
)
)
private A ref2a;
// ...
}
And the test code:
public class CascadeTest extends TestCase
{
private EntityManagerFactory emf;
private EntityManager em;
#Override
protected void setUp() throws Exception {
emf = Persistence.createEntityManagerFactory("myDB");
em = emf.createEntityManager();
}
#Override
protected void tearDown() throws Exception {
em.close();
emf.close();
}
public void testApp()
{
Integer aid = -1, bid = -1;
try {
em.getTransaction().begin();
A a = new A();
a.setName("My name is A");
B b = new B();
b.setRef2a(a);
b.setName("My name is B, please delete me when A is gone.");
em.persist(a);
em.persist(b);
em.getTransaction().commit();
aid = a.getId();
bid = b.getId();
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
try {
em.getTransaction().begin();
B b = em.find(B.class, bid);
assertNotNull(b);
assertEquals("My name is B, please delete me when A is gone.", b.getName());
assertEquals("My name is A", b.getRef2a().getName());
assertEquals(aid, b.getRef2a().getId());
A a = em.find(A.class, aid);
assertEquals("My name is A", a.getName());
em.remove(a);
em.getTransaction().commit();
em.getTransaction().begin();
// a should have been removed.
// This passes OK.
a = em.find(A.class, aid);
assertNull(a);
// Cascading deletes should have deleted also b.
b = em.find(B.class, bid);
// PROBLEM: This fails - b is still here.
assertNull(b);
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
}
}

I have solved my problem. Really really simple - my initial code was almost right. I just had a syntax problem in the foreign key cascade. The attributes needed to be in brackets "()", I had overlooked that in the documentation.
So the change I needed to do is:
#OneToOne
#JoinColumn(
foreignKey=#ForeignKey(
foreignKeyDefinition="FOREIGN KEY (ref2a) REFERENCES A (id) ON DELETE CASCADE"
)
)
private A ref2a;
Please notice the brackets around the two attributes.
This works, deleting an A object also cascades its linked B objects.
Thanks to everybody for your help!

EclipseLink provides a #CascadeOnDelete annotation that aligns with database "ON DELETE CASCADE" contraint. This annotation tells EclipseLink that the entity will be deleted by the database foriegn key constraint when this entity is deleted, and if using DDL, EclipseLink will generate the table with the proper constraint.
see https://wiki.eclipse.org/EclipseLink/Examples/JPA/DeleteCascade for details.
I think though that you can get by with a simple cascade delete on the FriendshipRelation.person mapping:
#Entity
public class FriendshipRelation {
..
#OneToOne(cascade=CascadeType.REMOVE)
private Person person;
This will force JPA to remove any referenced person when the FriendshipRelation instance is removed.

Related

Delete Not Working with JpaRepository

I have a spring 4 app where I'm trying to delete an instance of an entity from my database. I have the following entity:
#Entity
public class Token implements Serializable {
#Id
#SequenceGenerator(name = "seqToken", sequenceName = "SEQ_TOKEN", initialValue = 500, allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqToken")
#Column(name = "TOKEN_ID", nullable = false, precision = 19, scale = 0)
private Long id;
#NotNull
#Column(name = "VALUE", unique = true)
private String value;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "USER_ACCOUNT_ID", nullable = false)
private UserAccount userAccount;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "EXPIRES", length = 11)
private Date expires;
...
// getters and setters omitted to keep it simple
}
I have a JpaRepository interface defined:
public interface TokenRepository extends JpaRepository<Token, Long> {
Token findByValue(#Param("value") String value);
}
I have a unit test setup that works with an in memory database (H2) and I am pre-filling the database with two tokens:
#Test
public void testDeleteToken() {
assertThat(tokenRepository.findAll().size(), is(2));
Token deleted = tokenRepository.findOne(1L);
tokenRepository.delete(deleted);
tokenRepository.flush();
assertThat(tokenRepository.findAll().size(), is(1));
}
The first assertion passes, the second fails. I tried another test that changes the token value and saves that to the database and it does indeed work, so I'm not sure why delete isn't working. It doesn't throw any exceptions either, just doesn't persist it to the database. It doesn't work against my oracle database either.
Edit
Still having this issue. I was able to get the delete to persist to the database by adding this to my TokenRepository interface:
#Modifying
#Query("delete from Token t where t.id = ?1")
void delete(Long entityId);
However this is not an ideal solution. Any ideas as to what I need to do to get it working without this extra method?
Most probably such behaviour occurs when you have bidirectional relationship and you're not synchronizing both sides WHILE having both parent and child persisted (attached to the current session).
This is tricky and I'm gonna explain this with the following example.
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "parent")
private Set<Child> children = new HashSet<>(0);
public void setChildren(Set<Child> children) {
this.children = children;
this.children.forEach(child -> child.setParent(this));
}
}
#Entity
public class Child {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
#ManyToOne
#JoinColumn(name = "parent_id")
private Parent parent;
public void setParent(Parent parent) {
this.parent = parent;
}
}
Let's write a test (a transactional one btw)
public class ParentTest extends IntegrationTestSpec {
#Autowired
private ParentRepository parentRepository;
#Autowired
private ChildRepository childRepository;
#Autowired
private ParentFixture parentFixture;
#Test
public void test() {
Parent parent = new Parent();
Child child = new Child();
parent.setChildren(Set.of(child));
parentRepository.save(parent);
Child fetchedChild = childRepository.findAll().get(0);
childRepository.delete(fetchedChild);
assertEquals(1, parentRepository.count());
assertEquals(0, childRepository.count()); // FAILS!!! childRepostitory.counts() returns 1
}
}
Pretty simple test right? We're creating parent and child, save it to database, then fetching a child from database, removing it and at last making sure everything works just as expected. And it's not.
The delete here didn't work because we didn't synchronized the other part of relationship which is PERSISTED IN CURRENT SESSION. If Parent wasn't associated with current session our test would pass, i.e.
#Component
public class ParentFixture {
...
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void thereIsParentWithChildren() {
Parent parent = new Parent();
Child child = new Child();
parent.setChildren(Set.of(child));
parentRepository.save(parent);
}
}
and
#Test
public void test() {
parentFixture.thereIsParentWithChildren(); // we're saving Child and Parent in seperate transaction
Child fetchedChild = childRepository.findAll().get(0);
childRepository.delete(fetchedChild);
assertEquals(1, parentRepository.count());
assertEquals(0, childRepository.count()); // WORKS!
}
Of course it only proves my point and explains the behaviour OP faced. The proper way to go is obviously keeping in sync both parts of relationship which means:
class Parent {
...
public void dismissChild(Child child) {
this.children.remove(child);
}
public void dismissChildren() {
this.children.forEach(child -> child.dismissParent()); // SYNCHRONIZING THE OTHER SIDE OF RELATIONSHIP
this.children.clear();
}
}
class Child {
...
public void dismissParent() {
this.parent.dismissChild(this); //SYNCHRONIZING THE OTHER SIDE OF RELATIONSHIP
this.parent = null;
}
}
Obviously #PreRemove could be used here.
I had the same problem
Perhaps your UserAccount entity has an #OneToMany with Cascade on some attribute.
I've just remove the cascade, than it could persist when deleting...
You need to add PreRemove function ,in the class where you have many object as attribute e.g in Education Class which have relation with UserProfile
Education.java
private Set<UserProfile> userProfiles = new HashSet<UserProfile>(0);
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "educations")
public Set<UserProfile> getUserProfiles() {
return this.userProfiles;
}
#PreRemove
private void removeEducationFromUsersProfile() {
for (UsersProfile u : usersProfiles) {
u.getEducationses().remove(this);
}
}
One way is to use cascade = CascadeType.ALL like this in your userAccount service:
#OneToMany(cascade = CascadeType.ALL)
private List<Token> tokens;
Then do something like the following (or similar logic)
#Transactional
public void deleteUserToken(Token token){
userAccount.getTokens().remove(token);
}
Notice the #Transactional annotation. This will allow Spring (Hibernate) to know if you want to either persist, merge, or whatever it is you are doing in the method. AFAIK the example above should work as if you had no CascadeType set, and call JPARepository.delete(token).
This is for anyone coming from Google on why their delete method is not working in Spring Boot/Hibernate, whether it's used from the JpaRepository/CrudRepository's delete or from a custom repository calling session.delete(entity) or entityManager.remove(entity).
I was upgrading from Spring Boot 1.5 to version 2.2.6 (and Hibernate 5.4.13) and had been using a custom configuration for transactionManager, something like this:
#Bean
public HibernateTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new HibernateTransactionManager(entityManagerFactory.unwrap(SessionFactory.class));
}
And I managed to solve it by using #EnableTransactionManagement and deleting the custom
transactionManager bean definition above.
If you still have to use a custom transaction manager of sorts, changing the bean definition to the code below may also work:
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
As a final note, remember to enable Spring Boot's auto-configuration so the entityManagerFactory bean can be created automatically, and also remove any sessionFactory bean if you're upgrading to entityManager (otherwise Spring Boot won't do the auto-configuration properly). And lastly, ensure that your methods are #Transactional if you're not dealing with transactions manually.
I was facing the similar issue.
Solution 1:
The reason why the records are not being deleted could be that the entities are still attached. So we've to detach them first and then try to delete them.
Here is my code example:
User Entity:
#Entity
public class User {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
private List<Contact> contacts = new ArrayList<>();
}
Contact Entity:
#Entity
public class Contact {
#Id
private int cId;
#ManyToOne
private User user;
}
Delete Code:
user.getContacts().removeIf(c -> c.getcId() == contact.getcId());
this.userRepository.save(user);
this.contactRepository.delete(contact);
Here we are first removing the Contact object (which we want to delete) from the User's contacts ArrayList, and then we are using the delete() method.
Solution 2:
Here we are using the orphanRemoval attribute, which is used to delete orphaned entities from the database. An entity that is no longer attached to its parent is known as an orphaned entity.
Code example:
User Entity:
#Entity
public class User {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = true)
private List<Contact> contacts = new ArrayList<>();
}
Contact Entity:
#Entity
public class Contact {
#Id
private int cId;
#ManyToOne
private User user;
}
Delete Code:
user.getContacts().removeIf(c -> c.getcId() == contact.getcId());
this.userRepository.save(user);
Here, as the Contact entity is no longer attached to its parent, it is an orphaned entity and will be deleted from the database.
I just went through this too. In my case, I had to make the child table have a nullable foreign key field and then remove the parent from the relationship by setting null, then calling save and delete and flush.
I didn't see a delete in the log or any exception prior to doing this.
If you use an newer version of Spring Data, you could use deleteBy syntax...so you are able to remove one of your annotations :P
the next thing is, that the behaviour is already tract by a Jira ticket:
https://jira.spring.io/browse/DATAJPA-727
#Transactional
int deleteAuthorByName(String name);
you should write #Transactional in Repository extends JpaRepository
Your initial value for id is 500. That means your id starts with 500
#SequenceGenerator(name = "seqToken", sequenceName = "SEQ_TOKEN",
initialValue = 500, allocationSize = 1)
And you select one item with id 1 here
Token deleted = tokenRepository.findOne(1L);
So check your database to clarify that
I've the same problem, test is ok but on db row isn't deleted.
have you added the #Transactional annotation to method? for me this change makes it work
In my case was the CASCADE.PERSIST, i changed for CASCADE.ALL, and made the change through the cascade (changing the father object).
CascadeType.PERSIST and orphanRemoval=true doesn't work together.
Try calling deleteById instead of delete on the repository. I also noticed that you are providing an Optional entity to the delete (since findOne returns an Optional entity). It is actually strange that you are not getting any compilation errors because of this. Anyways, my thinking is that the repository is not finding the entity to delete.
Try this instead:
#Test
public void testDeleteToken() {
assertThat(tokenRepository.findAll().size(), is(2));
Optional<Token> toDelete = tokenRepository.findOne(1L);
toDelete.ifExists(toDeleteThatExists -> tokenRepository.deleteById(toDeleteThatExists.getId()))
tokenRepository.flush();
assertThat(tokenRepository.findAll().size(), is(1));
}
By doing the above, you can avoid having to add the #Modifying query to your repository (since what you are implementing in that #Modifying query is essentially the same as calling deleteById, which already exists on the JpaRepository interface).

deleting entity in a onetomany relationship

I'm having a problem deleting a child entity item. Everytime I delete it nothing happens and the association between the parent and the child is still there. I've searched through the net and some people suggest using orphanremoval but I've tried it and it didn't work. Appreciate if any could advise.
My codes as below:
ClientProfile Entity (PARENT)
Collapse | Copy Code
#Entity (name="ClientProfile")
public class ClientProfile implements Serializable {
#OneToMany(orphanRemoval = true)
private List<Address> address;
#OneToMany(orphanRemoval = true)
private List<ClientJob> clientJob;
#OneToMany(orphanRemoval = true)
private List<Asset> clientAsset;
...
}
Asset Entity (CHILD)
is a uni-directional relationship so asset entity doesnt contain any #ManyToOne
In my SQL Database table my relationship is CLIENTPROFILE_CLIENTASSET
adn they are connected by the clientid to the assetid
In my session bean this is my remove method:
#Override
public void removeAsset(Long assetId) throws DoesNotExistsException{
Query query = em.createQuery("SELECT as FROM Asset as WHERE as.assetId = :assetid");
query.setParameter("assetid", assetId);
if (query.getResultList().isEmpty()){
throw new DoesNotExistsException("Asset does not exist!");
} else {
em.remove(query.getSingleResult());
}
}
the assetid is being parsed into from the managedbean.
I'm not sure if the remove method is wrong because this is the method I used to remove other entities items without relationship.
This should help you. It is along the same lines. I usually utilize Hibernate instead of JPA to get automatic deletion through cascade attributes.
JPA OneToMany not deleting child

Save child objects automatically using JPA Hibernate

I have a one-to-many relation between Parent and Child table. In the parent object I have a
List<Child> setChildren(List<Child> childs)
I also have a foreign key in the Child table. This foreign key is an ID that references a Parent row in database. So in my database configuration this foreign key can not be NULL.
Also this foreign key is the primary key in the Parent table.
So my question is how I can automatically save the children objects by doing something like this:
session.save(parent);
I tried the above but I'm getting a database error complaining that the foreign key field in the Child table can not be NULL. Is there a way to tell JPA to automatically set this foreign key into the Child object so it can automatically save children objects?
I tried the above but I'm getting a database error complaining that the foreign key field in the Child table can not be NULL. Is there a way to tell JPA to automatically set this foreign key into the Child object so it can automatically save children objects?
Well, there are two things here.
First, you need to cascade the save operation (but my understanding is that you are doing this or you wouldn't get a FK constraint violation during inserts in the "child" table)
Second, you probably have a bidirectional association and I think that you're not setting "both sides of the link" correctly. You are supposed to do something like this:
Parent parent = new Parent();
...
Child c1 = new Child();
...
c1.setParent(parent);
List<Child> children = new ArrayList<Child>();
children.add(c1);
parent.setChildren(children);
session.save(parent);
A common pattern is to use link management methods:
#Entity
public class Parent {
#Id private Long id;
#OneToMany(mappedBy="parent")
private List<Child> children = new ArrayList<Child>();
...
protected void setChildren(List<Child> children) {
this.children = children;
}
public void addToChildren(Child child) {
child.setParent(this);
this.children.add(child);
}
}
And the code becomes:
Parent parent = new Parent();
...
Child c1 = new Child();
...
parent.addToChildren(c1);
session.save(parent);
References
Hibernate Core Reference Guide
1.2.6. Working bi-directional links
I believe you need to set the cascade option in your mapping via xml/annotation. Refer to Hibernate reference example here.
In case you are using annotation, you need to do something like this,
#OneToMany(cascade = CascadeType.PERSIST) // Other options are CascadeType.ALL, CascadeType.UPDATE etc..
Following program describe how bidirectional relation work in hibernate.
When parent will save its list of child object will be auto save.
On Parent side:
#Entity
#Table(name="clients")
public class Clients implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#OneToMany(mappedBy="clients", cascade=CascadeType.ALL)
List<SmsNumbers> smsNumbers;
}
And put the following annotation on the child side:
#Entity
#Table(name="smsnumbers")
public class SmsNumbers implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
String number;
String status;
Date reg_date;
#ManyToOne
#JoinColumn(name = "client_id")
private Clients clients;
// and getter setter.
}
Main class:
public static void main(String arr[])
{
Session session = HibernateUtil.openSession();
//getting transaction object from session object
session.beginTransaction();
Clients cl=new Clients("Murali", "1010101010");
SmsNumbers sms1=new SmsNumbers("99999", "Active", cl);
SmsNumbers sms2=new SmsNumbers("88888", "InActive", cl);
SmsNumbers sms3=new SmsNumbers("77777", "Active", cl);
List<SmsNumbers> lstSmsNumbers=new ArrayList<SmsNumbers>();
lstSmsNumbers.add(sms1);
lstSmsNumbers.add(sms2);
lstSmsNumbers.add(sms3);
cl.setSmsNumbers(lstSmsNumbers);
session.saveOrUpdate(cl);
session.getTransaction().commit();
session.close();
}
in your setChilds, you might want to try looping thru the list and doing something like
child.parent = this;
you also should set up the cascade on the parent to the appropriate values.
Here are the ways to assign parent object in child object of Bi-directional relations ?
Suppose you have a relation say One-To-Many,then for each parent object,a set of child object exists.
In bi-directional relations,each child object will have reference to its parent.
eg : Each Department will have list of Employees and each Employee is part of some department.This is called Bi directional relations.
To achieve this, one way is to assign parent in child object while persisting parent object
Parent parent = new Parent();
...
Child c1 = new Child();
...
c1.setParent(parent);
List<Child> children = new ArrayList<Child>();
children.add(c1);
parent.setChilds(children);
session.save(parent);
Other way is, you can do using hibernate Intercepter,this way helps you not to write above code for all models.
Hibernate interceptor provide apis to do your own work before perform any DB operation.Likewise onSave of object, we can assign parent object in child objects using reflection.
public class CustomEntityInterceptor extends EmptyInterceptor {
#Override
public boolean onSave(
final Object entity, final Serializable id, final Object[] state, final String[] propertyNames,
final Type[] types) {
if (types != null) {
for (int i = 0; i < types.length; i++) {
if (types[i].isCollectionType()) {
String propertyName = propertyNames[i];
propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
try {
Method method = entity.getClass().getMethod("get" + propertyName);
List<Object> objectList = (List<Object>) method.invoke(entity);
if (objectList != null) {
for (Object object : objectList) {
String entityName = entity.getClass().getSimpleName();
Method eachMethod = object.getClass().getMethod("set" + entityName, entity.getClass());
eachMethod.invoke(object, entity);
}
}
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
return true;
}
}
And you can register Intercepter to configuration as
new Configuration().setInterceptor( new CustomEntityInterceptor() );
In JPA #*To* relationships both parent and child entities must be cross assigned before (parent) saving.
Use org.hibernate.annotations for doing Cascade , if the hibernate and JPA are used together , its somehow complaining on saving the child objects.
In short set cascade type to all , will do a job;
For an example in your model.
Add Code like this .
#OneToMany(mappedBy = "receipt", cascade=CascadeType.ALL)
private List saleSet;
If you do not have bidirectional relationship and want to only save/update the the single column in the child table, then you can create JPA repository with child Entity and call save/saveAll or update method.
Note: if you come across FK violations then it means your postman request having primary and foreign key ids is not matching with generated ids in child table , check the ids in your request and child table which your are going to update(they should match/if they don't means you get FK violations) whatever ids generated while saving the parent and child in before transactions, those ids should match in your second call when you try to update the single column in your child table.
Parent:
#Entity
#Table(name="Customer")
public class Customer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private UUID customerId ;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name ="child_columnName", referencedColumnName=
"parent_columnName")
List<Accounts> accountList;
}
Child :
#Entity
#Table(name="Account")
public class Account implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private UUID accountid;
}

OneToMany Annotated Collection Not Persisting via Hibernate

I currently am trying to persist a collection using #OneToMany(cascade=CascadeType.ALL) for a simple list of objects. The table for Parent_Child gets created in MySQL but the keys for each object are not updated upon using SaveOrUpdate. Any idea what the issue is? (My parent key is defined and the children are generated). I add the children to the parent object's collection before persisting with saveOrUpdate. I'm using MySQL with hibernate 3 and my auto property is set to create-drop.
The test class:
public class Tester {
public static void main(String[] args) {
VideoChannel testChannel = new VideoChannel("Test Channel");
VideoChannelMap v = new VideoChannelMap(testChannel, "Test Map");
VideoSource sc2Vid = new VideoSource("starcraft-ii-ghost-of-the-past.mp4", "EinghersStreamingBucket");
testChannel.add(sc2Vid);
Session s = HibernateSessionFactory.getSession();
s.beginTransaction();
s.saveOrUpdate(v);
s.close();
}
}
The entities:
#Entity
public class VideoChannelMap {
#Id
String name;
#OneToMany(cascade=CascadeType.ALL)
List<VideoChannel> channelMap;
public VideoChannelMap(VideoChannel initialVid, String name)
{
this.name = name;
channelMap = new ArrayList<VideoChannel>();
channelMap.add(initialVid);
initialVid.setParent(this);
}
}
#Entity
public class VideoChannel {
#Id #GeneratedValue
Long id;
...
}
You have to actually commit your transaction. The behavior when you close a session with a transaction still open isn't very well defined and will likely depend on how your database is set up underneath.
Transaction t = s.beginTransaction();
s.saveOrUpdate(v);
t.commit();
s.close();
Obviously you should also have some try-catch-finally action going on in there for "real" code ;)

Delete One To One relationship

I have these classes.
#Entity
#Table(name ="a")
class A{
private Integer aId;
private B b;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id_a")
public Integer getAId() {
return aId;
}
#OneToOne(mappedBy = "a", cascade={CascadeType.ALL})
public B getB() {
return b;
}
}
#Entity
#Table (name= "b")
class B{
private A a;
#OneToOne
#JoinColumn(name = "id_a")
public A getA() {
return a;
}
}
And tables look like:
A) | id_A |....other fields...|
B) | id_B | fk_id_A |..other fields..|
But, when I try to delete an instance of A, I get,
org.hibernate.ObjectDeletedException:
deleted object would be re-saved by
cascade :
(remove deleted object from associations)[B#130]
I've tried setting null on cross references:
b.setA(null)
a.setB(null)
But exception still gets thrown.
All that I have accomplished is to delete a row in A, and leave B's foreign Key null, but when i re try to delete B, get the same error.
This is my delete code:
public static void delete(Object object) throws Exception {
Transaction tx = getSession().beginTransaction();
try {
getSession().delete(object);
tx.commit();
} catch (Exception ex) {
tx.rollback();
getSession().close();
throw ex;
}
}
getSession always returns a valid session.
Is there anything I'm missing?
Start from top and continue working down. You need to delete the reference to A in table B. So locate the reference in B of the record you're trying to delete in A and delete that record in B. Then go back to A and delete the record in A.
If you're using SQL Server, you can set cascading delete on table A and this will be done for you automatically. You have to be careful with this though. I wouldn't recommend this for complex structures.
Are you performing the nulling of cross-references in a separate transaction? Those changes may need to be committed for the delete to work.

Categories

Resources