2015-01-28 4 views
1

Мой вопрос о каскадных удалениях с помощью JPA и Eclipselink.jpa cascading удаляет обратную связь

Я хотел бы, чтобы смоделировать простую связь между двумя сущностями: A и B. B ссылки А через свойство ref2a (в терминах DB B.ref2a подключен к A.id через внешний ключ с «ON DELETE CASCADE»). Моя цель - когда объект A удаляется, чтобы каскадировать удаление ко всем объектам B, которые ссылаются на него.

Я много искал, но я не могу заставить его работать. Большинство решений, которые я нашел, предназначены для противоположной ситуации: A содержит набор ссылок на B. Это работает как шарм. Но если ссылка находится на стороне B, я не знаю, как это сделать.

Вот пример кода:

@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(
      [email protected](
        foreignKeyDefinition="FOREIGN KEY ref2a REFERENCES A id ON DELETE CASCADE" 
        ) 
      ) 
    private A ref2a; 
    // ... 
} 

И тестовый код:

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(); 
     } 

    } 
} 

ответ

1

Я решил свою проблему. На самом деле очень простой - мой первоначальный код был почти прав. У меня просто была синтаксическая проблема во внешнем каскаде ключей. Атрибуты должны быть в скобках «()», я не обратил внимания на это в документации. Поэтому изменение мне нужно сделать, это:

@OneToOne 
    @JoinColumn(
      [email protected](
        foreignKeyDefinition="FOREIGN KEY (ref2a) REFERENCES A (id) ON DELETE CASCADE" 
        ) 
      ) 
    private A ref2a; 

Пожалуйста, обратите внимание на скобки вокруг двух атрибутов.

Это работает, удаление объекта A также каскадирует связанные объекты B.

Спасибо всем за вашу помощь!

-1

EclipseLink обеспечивает @CascadeOnDelete аннотацию, которая совпадет с базой данных "ON DELETE CASCADE" contraint. Эта аннотация указывает EclipseLink, что объект будет удален с помощью ограничения ключа foriegn базы данных, когда этот объект будет удален, а при использовании DDL EclipseLink будет генерировать таблицу с соответствующим ограничением. см. https://wiki.eclipse.org/EclipseLink/Examples/JPA/DeleteCascade для деталей.

Я думаю, что, хотя вы можете получить с помощью простого каскада удаления на отображение FriendshipRelation.person:

@Entity 
public class FriendshipRelation { 
.. 
    @OneToOne(cascade=CascadeType.REMOVE) 
    private Person person; 

Это заставит JPA, чтобы удалить ссылается лицо, когда экземпляр FriendshipRelation удаляется.

+1

Привет, Крис, спасибо за ваш ответ. Я пробовал @CascadeOnDelete, но он не работает. Кстати, я хочу сделать противоположное тому, что вы описали. Я не хочу удалять ссылку. Я хочу удалить все ссылки FriendshipRelations, которые связаны с удаленным человеком через вторую ссылку (один на один обратный), а не через список «friendlyRelations». – mbax

+0

Аннотации @CascadeOnDelete не удаляют дочерние строки для вас. Он просто сообщает JPA, что база данных удалит их. Если у вас не было настройки каскада delete в схеме DB, это не сработает. –

+0

@mbax, проблема неясна, потому что вы включили OneToMany, которая не связана - попробуйте удалить ее из описания проблемы. Похоже, проблема заключается в том, что у вас есть одно сопоставление OneToOne, и вы хотите, чтобы сторона ссылки удалялась, когда удаленный человек был удален. Если это так, то вам нужно добавить отображение OneToOne от Person to FriendshipRelation, «отображаемое» в RelianceRelation.отношения человека, делая его двунаправленным отношением. В противном случае JPA не может узнать о экземпляре FriendshipRelation, который вы хотите удалить. Затем вы можете отметить это с помощью cascade.remove. – Chris