# Hibernate Bi-Directional delete



## -MacNuke- (20. Jul 2008)

Hallo.

Ich probiere gerade wieder mit etwas Hibernate rum, aber hänge jetzt fest.

Aus verschiedenen Beispiel-Anwendungen habe ich mir folgendes zusammengebastelt:

Question.java

```
@Entity
@Table(name = "QUESTION")
public class Question {

    private Long id;
    private String text;
    private List<Choice> choices = new ArrayList();

    Question() {
    }
    
    public Question(String text) {
        this.setText(text);        
    }
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "QUESTION_ID")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Column(name = "TEXT")
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
    
    @OneToMany(cascade = {CascadeType.ALL})
    @JoinColumn(name = "QUESTION_ID")
    @org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    public List<Choice> getChoices() {
        return choices;
    }

    public void setChoices(List<Choice> choices) {
        this.choices = choices;
    }

}
```

Choice.java

```
@Entity
@Table(name = "CHOICE")
public class Choice {

    private Long id;
    private String text;
    private Question question;

    Choice() {
    }
    
    public Choice(String text) {
        this.setText(text);
    }    

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "CHOICE_ID")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Column(name = "TEXT")
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    @ManyToOne
    @JoinColumn (name="QUESTION_ID", nullable = false)
    public Question getQuestion() {
        return question;
    }

    public void setQuestion(Question question) {
        this.question = question;
    }
        
}
```


Ist eine Bi-Directionale Beziehung zwischen Frage und Antworten. Nun möchte ich eine Frage löschen und laut allem was ich gefunden habe, sollte er die dazugehörigen Antworten ebenso löschen. Aber das klappt nicht (action6):



```
public class HelloWorld {

    public static void action1() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = session.beginTransaction();

        Question q = new Question("Ist das ein Test?");
        session.save(q);
        Choice c = new Choice("Ja!");
        Choice c2 = new Choice("Nein!");
        c.setQuestion(q);
        c2.setQuestion(q);

        session.save(c);
        session.save(c2);

        q.getChoices().add(c);
        q.getChoices().add(c2);

        tx.commit();
        session.close();
    }

    public static void action2() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = session.beginTransaction();

        Question q = (Question) session.get(Question.class, new Long(2));
        q.setText("Das ist toll!!! Oder?");

        tx.commit();
        session.close();
    }

    public static void action3() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = session.beginTransaction();

        Question q = (Question) session.get(Question.class, new Long(2));
        Choice c = new Choice("Das muss klappen.");
        c.setQuestion(q);
        q.getChoices().add(c);

        tx.commit();
        session.close();
    }

    public static void action4() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = session.beginTransaction();

        Question q = (Question) session.get(Question.class, new Long(2));

        List<Choice> l = q.getChoices();

        for (Choice choice : l) {
            System.out.println(choice.getText());
        }

        tx.commit();
        session.close();
    }

    public static void action5() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = session.beginTransaction();

        Question q = (Question) session.get(Question.class, new Long(2));

        Choice c = new Choice("Dindidumdidum");
        c.setQuestion(q);
        session.save(c);

        tx.commit();
        session.close();
    }

    public static void action6() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = session.beginTransaction();

        Long i = (Long) session.createQuery("select max(q.id) as lastrecid from Question q").uniqueResult();

        System.out.println(">>>>> " + i);

        Question q = (Question) session.load(Question.class, i);
        session.delete(q);

        tx.commit();
        session.close();
    }

    public static void main(String[] args) {
        action1();
        System.out.println("------------------------------------------");
        action2();
        System.out.println("------------------------------------------");
        action3();
        System.out.println("------------------------------------------");
        action4();
        System.out.println("------------------------------------------");
        action5();
        System.out.println("------------------------------------------");
        action6();
}
```


Also action 1 bis 5 funktionieren super. Aber beim Löschen kommt folgende Meldung (nur Ausgabe von action6):



```
Hibernate: 
    select
        max(question0_.QUESTION_ID) as col_0_0_ 
    from
        QUESTION question0_
>>>>> 1031
Hibernate: 
    select
        question0_.QUESTION_ID as QUESTION1_0_0_,
        question0_.TEXT as TEXT0_0_ 
    from
        QUESTION question0_ 
    where
        question0_.QUESTION_ID=?
Hibernate: 
    select
        choices0_.QUESTION_ID as QUESTION3_1_,
        choices0_.CHOICE_ID as CHOICE1_1_,
        choices0_.CHOICE_ID as CHOICE1_1_0_,
        choices0_.QUESTION_ID as QUESTION3_1_0_,
        choices0_.TEXT as TEXT1_0_ 
    from
        CHOICE choices0_ 
    where
        choices0_.QUESTION_ID=?
Hibernate: 
    update
        CHOICE 
    set
        QUESTION_ID=null 
    where
        QUESTION_ID=?
21:18:04,334 WARN JDBCExceptionReporter:77 - SQL Error: 0, SQLState: null
21:18:04,335ERROR JDBCExceptionReporter:78 - failed batch
21:18:04,336ERROR AbstractFlushingEventListener:301 - Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
        at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
Exception in thread "main" org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
        at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
        at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
        at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
        at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:169)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
        at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
        at hello.HelloWorld.action6(Unknown Source)
        at hello.HelloWorld.main(Unknown Source)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:169)
Caused by: java.sql.BatchUpdateException: failed batch
        at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source)
        at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
        at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
        ... 9 more
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at hello.HelloWorld.action6(Unknown Source)
        at hello.HelloWorld.main(Unknown Source)
Caused by: java.sql.BatchUpdateException: failed batch
        at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source)
        at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source)
        at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
        ... 9 more
Java Result: 1
```

Ich habe schon bei Google gesucht und diverse Sachen gelesen, aber ich weiß echt nicht was ich machen soll, bzw. woran es liegt.

Was fehlt denn noch? Was ist falsch?

Wäre echt super, wenn mir jemand helfen könnte 

Danke


----------



## -MacNuke- (20. Jul 2008)

Hmmm... wenn ich das nullable=false entferne, dann geht es. Da sieht man, dass Hibernate zuerst alle Choices mit Null beschreibt, bei der Beziehnung. Wozu denn das? Der löscht die doch direkt danach...


----------



## byte (21. Jul 2008)

Mach mal in Question die Kaskadierung auf _all, delete-orphan_. Dann ist Question ein so genanntes Life Cycle Objekt. Kommt die Exception dann auch?



> If the child object's lifespan is bounded by the lifespan of the of the parent object make it a lifecycle object
> by specifying cascade="all,delete-orphan".


----------



## -MacNuke- (21. Jul 2008)

Hi.

Danke dafür 

Das gehört auch dazu. Ich habe jetzt die Lösung zusammengefunden. Erst mal der Teil von dir und dann muss man noch das mappedBy angeben. So muss das Mapping aussehen:

Question.java

```
@OneToMany(mappedBy="question", cascade = {CascadeType.ALL})
@JoinColumn(name = "QUESTION_ID")
@Cascade({org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
public List<Choice> getChoices() {
    return choices;
}
```

Choice.java


```
@ManyToOne
@OnDelete(action = org.hibernate.annotations.OnDeleteAction.CASCADE)
@JoinColumn (name="QUESTION_ID", nullable = false)
public Question getQuestion() {
    return question;
}
```


----------

