Using Ebean/JPA in my Play application, how can I delete an object in a OneToOne relationship?
Asked Answered
L

2

2

I have the following classes:

import play.db.ebean.Model;
import javax.persistence.*;

@Entity
public class A extends Model {
    @Id
    private int id;

    /* Other irrelevant properties */

    @OneToOne(cascade = CascadeType.ALL, optional = true)
    private B b;
}

import play.db.ebean.Model;
import javax.persistence.*;

@Entity
public class B extends Model {
    @Id
    private int id;

    /* Other irrelevant properties */

    @OneToOne(mappedBy = "b")
    private A a;

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    private C c;
}

import play.db.ebean.Model;
import javax.persistence.*;

@Entity
public class C extends Model {
    @Id
    private int id;

    /* Other irrelevant properties */

    @OneToOne(mappedBy = "c")
    private B B;
}

In the database, it looks like this:

Table    a
Columns  id, ... (other irrelevant columns), b_id

Table    b
Columns  id, ...(other irrelevant columns), c_id

Table    c
Columns  id, ...(other irrelevant columns)

Now in one the controller methods (for those familiar with Play) I have an object of A, and would like to delete its property "b" from the database. This should also cascade to c, so c gets deleted too. This is my current approach:

B b = a.getB();
b.delete();

This throws an exception though:

[PersistenceException: ERROR executing DML bindLog[] error[Cannot delete or update a parent row: a foreign key constraint fails (databasename.a, CONSTRAINT fk_a_payme_4 FOREIGN KEY (b_id) REFERENCES b (id))]]

It basically boils down to the fact that I'm trying to delete b, while a still holds a foreign key reference to b in column b_id, so I should first set that reference to null.

In Java, this translates to:

B b = a.getB();
a.setB(null);
a.update();
b.delete();

This does set the reference to b in object a to null and correctly deletes the b object, but c does not get deleted from the database. (Why? I though the cascade property would take care of this) The only way I have found to fix this is to explicitly delete c as well, so like this:

B b = a.getB();
C c = b.getC();

b.setC(null);
b.update();
c.delete();

a.setB(null);
a.update();
b.delete();

I'm not very pleased about this though, because that's already 8 lines of code to delete two rows in the database, and it would be more if there were more relationships in this picture.

So as for my question:

How can I delete b from a so the reference from a to b is deleted automatically first and how can I make sure c is deleted too when I delete b?

Thank you in advance!

Edit: best idea so far: move the ownership of the a-b relationship to b.

Leggett answered 23/8, 2012 at 13:45 Comment(0)
T
2

Try using

class A{

   ......

   @JoinColumn(nullable=true)
   private B b;

   .......
}

See this question for more info What is the difference between @ManyToOne(optional=false) vs. @Column(nullable=false).

Textualist answered 23/8, 2012 at 13:57 Comment(4)
How is this different from @OneToOne(optional = true) ?Leggett
Thanks for the link, I learned something new again. I don't see how this could solve my problem though, when I look at the database design it says column 'b_id' in table 'a' is nullable, so this shouldn't change anything, right?Leggett
So cascading rules simply don't work in Ebean? Alright, at least I can start looking for a better solution then. Thanks, I've accepted this.Leggett
Cascading rules does work, its not there in generated DDL. You will have to manually edit the generated DDL. All generated DDL are there in <project folder>/conf/evolutions/<database name>. You can use Hibernate as an alternative.Textualist
S
-1

I run into some issue with @johny answer. Try this.

@JoinColumn(name = "b_id", nullable = false)
Sterner answered 23/1, 2013 at 10:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.