Сироты остаются в базе данных даже с orphanRemoval = true для отношения один-ко-многим (JPA / Hibernate) - PullRequest
12 голосов
/ 22 июля 2010
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "company_policies")
@DiscriminatorColumn(name = "rule_name")
public abstract class AbstractPolicyRule implements Serializable {

  @Transient
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue
  private Long id;
  private String value;

  ...
}

_

@Entity
public class Category implements Serializable {

  @Transient
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue
  private Long id;
  @Column(name = "category_name")
  private String name;

  @OneToMany(fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval = true)
  @JoinColumn(name = "category_policy_id", referencedColumnName = "id")
  private Set<AbstractPolicyRule> activePolicyRules;

  ...
}

Когда этот Набор обновляется, для существующих activePolicyRules их category_policy_id в базе данных устанавливается значение null, и новые вставляются.Я хотел бы, чтобы оригинальные были удалены.

Я думал, что добавление orphanRemoval = true сделает это, но это не так.Другие вопросы, которые я видел по этому вопросу, имеют двунаправленные отношения, и установка родительского элемента на ноль решает его, но это не двунаправленное отношение.

Есть предложения?

ИспользованиеHibernate 3.5.3

Редактировать: это происходит только в том случае, если в базе данных существует существующий AbstractPolicyRule, я удаляю его из списка и затем снова сохраняю категорию.Его внешний ключ, category_policy_id, имеет значение NULL вместо удаления.

[DEBUG] Collection found: [domain.category.Category.activePolicyRules#1], was: 
[<unreferenced>] (initialized)
[DEBUG] Flushed: 0 insertions, 2 updates, 0 deletions to 2 objects
[DEBUG] Flushed: 1 (re)creations, 0 updates, 1 removals to 1 collections
...
[DEBUG] Deleting collection: [domain.category.Category2.activePolicyRules#1]
[DEBUG] about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
[DEBUG] update company_policies set category_policy_id=null where category_policy_id=?
[DEBUG] done deleting collection

Также пробовал таблицу соединения, так как документация Hibernate препятствует предыдущему способу:

@Entity
public class Category implements Serializable {

  @Transient
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue
  private Long id;
  @Column(name = "category_name")
  private String name;

  @OneToMany(fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval = true)
  @JoinTable(name = "policy_rule_mapping", 
    joinColumns = @JoinColumn(name = "category_id"), 
    inverseJoinColumns = @JoinColumn(name = "rule_id"))
  private Set<AbstractPolicyRule> activePolicyRules;

  ...
}

Это имеетта же проблема.Строка в таблице сопоставления удалена, но AbstractPolicyRule по-прежнему содержит удаленный элемент.

Ответы [ 2 ]

9 голосов
/ 22 июля 2010

Я использую orphanRemoval=true с однонаправленной ассоциацией «один ко многим» без проблем.

И на самом деле я протестировал ваш код и следующий сценарий (AbstractPolicyRule правильно реализует equals / hashCode):

Category category = new Category();
AbstractPolicyRule policyRule1 = new AbstractPolicyRule("foo");

category.addToActivePolicyRules(policyRule1);
em.persist(category);
em.flush();

assertNotNull(category.getId());
assertNotNull(category.getActivePolicyRules());
assertEquals(1, category.getActivePolicyRules().size());

category.removeFromActivePolicyRules(policyRule1);
category.addToActivePolicyRules(new AbstractPolicyRule("bar"));
// category = em.merge(category); // works with or without
em.flush();
assertEquals(1, category.getActivePolicyRules().size());

просто работает как положено. Ниже сгенерированных следов:

22:54:30.817 [main] DEBUG org.hibernate.SQL - insert into Category (id, category_name) values (null, ?)
Hibernate: insert into Category (id, category_name) values (null, ?)
22:54:30.824 [main] TRACE org.hibernate.type.StringType - binding null to parameter: 1
22:54:30.844 [main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 1
...
22:54:30.872 [main] DEBUG org.hibernate.SQL - insert into AbstractPolicyRule (id, name) values (null, ?)
Hibernate: insert into AbstractPolicyRule (id, name) values (null, ?)
22:54:30.873 [main] TRACE org.hibernate.type.StringType - binding 'foo' to parameter: 1
22:54:30.874 [main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 1
...
22:54:30.924 [main] DEBUG org.hibernate.SQL - update AbstractPolicyRule set category_policy_id=? where id=?
Hibernate: update AbstractPolicyRule set category_policy_id=? where id=?
22:54:30.927 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 1
22:54:30.928 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 2
22:54:30.929 [main] DEBUG o.h.p.c.AbstractCollectionPersister - done inserting collection: 1 rows inserted
22:54:30.929 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
...
22:54:30.945 [main] DEBUG org.hibernate.SQL - insert into AbstractPolicyRule (id, name) values (null, ?)
Hibernate: insert into AbstractPolicyRule (id, name) values (null, ?)
22:54:30.948 [main] TRACE org.hibernate.type.StringType - binding 'bar' to parameter: 1
22:54:30.948 [main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 2
...
22:54:30.990 [main] DEBUG org.hibernate.SQL - update AbstractPolicyRule set category_policy_id=null where category_policy_id=? and id=?
Hibernate: update AbstractPolicyRule set category_policy_id=null where category_policy_id=? and id=?
22:54:30.991 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 1
22:54:30.992 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 2
22:54:30.993 [main] DEBUG o.h.p.c.AbstractCollectionPersister - done deleting collection rows: 1 deleted
22:54:30.993 [main] DEBUG o.h.p.c.AbstractCollectionPersister - Inserting rows of collection: [com.stackoverflow.q3304092.Category.activePolicyRules#1]
22:54:30.994 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
...
22:54:30.996 [main] DEBUG org.hibernate.SQL - update AbstractPolicyRule set category_policy_id=? where id=?
Hibernate: update AbstractPolicyRule set category_policy_id=? where id=?
22:54:30.997 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 1
22:54:30.998 [main] TRACE org.hibernate.type.LongType - binding '2' to parameter: 2
22:54:31.002 [main] DEBUG o.h.p.c.AbstractCollectionPersister - done inserting rows: 1 inserted
...
22:54:31.015 [main] DEBUG org.hibernate.SQL - delete from AbstractPolicyRule where id=?
Hibernate: delete from AbstractPolicyRule where id=?
22:54:31.017 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 1

Первое правило политики удаляется.

Если это не соответствует тому, что вы делаете, возможно, вам следует предоставить больше кода.

Обновление: Ответ на комментарий от ОП ...

Ух, я только что изменил вызов saveOrUpdate для слияния, и теперь он удаляется соответствующим образом. У вас есть понимание, почему это так?

Просто предположение: поскольку orphanRemoval - вещь JPA, мне интересно, справится ли saveOrUpdate с ней соответствующим образом (на самом деле, я думал, что вы используете EntityManager API, так как вы упомянули JPA).

4 голосов
/ 22 июля 2010

Сначала убедитесь, что ваши классы реализуют методы hashCode() и equals(), чтобы hibernate знал, что именно эти элементы удалены из набора.

Затем попробуйте определить hibernate @Cascade аннотацию, указав там тип каскада удаления-сироты и наблюдающего, происходит ли то же самое. Если это работает так, как вы хотите - сообщите об ошибке в спящем режиме и временно используйте проприетарную аннотацию. В противном случае - уточните вопрос с деталями

...