Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Hibernate & cascade DELETE, Некорректное каскадное удаление 
V
    Опции темы
MisterCleric
Дата 13.7.2009, 17:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1043
Регистрация: 16.2.2006
Где: Харьков, Украина

Репутация: 33
Всего: 38



Привет, ребята. выручайте теперь и меня.
В обще есть у меня такая структура. Пусть простит меня Заказчик! Но блин, надо срочно. Вот такие сущности:
(сначала покажу, что есть потом скажу в чем трабла)
Код

@Entity
@org.hibernate.annotations.AccessType("field")
public class Bank implements Serializable {
  @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.REMOVE, CascadeType.MERGE})
    @JoinColumn(name = "BANKID")
    private List<BankInsuranceScheme> schemeRiskGroups = new ArrayList<BankInsuranceScheme>();
}

@Entity
@org.hibernate.annotations.AccessType("field")
@IdClass(BankInsuranceSchemePk.class)
public class BankInsuranceScheme implements Serializable {
    @Id
    private Long bankId;

    @Id
    private Long insuranceSchemeId;

   
    @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.REMOVE, CascadeType.MERGE})
    @JoinColumns({
            @JoinColumn(name = "BANKID"),
            @JoinColumn(name = "INSURANCESCHEMEID")
    })
    private Set<BankInsuranceschemeRiskGroup> riskGroups = new HashSet<BankInsuranceschemeRiskGroup>();
}

@Entity
@Table(name = "BANKINSURANCESCHEMERISK")
@org.hibernate.annotations.AccessType("field")
@IdClass(BankInsuranceschemeRiskGroupPk.class)
public class BankInsuranceschemeRiskGroup implements Serializable{
    @Id
    private Long bankId;

    @Id
    private Long insuranceSchemeId;

    @Id
    private Long insuranceRiskGroupId;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "INSURANCERISKGROUPID", insertable = false, updatable = false)
    private RiskGroup riskGroup;

    @Enumerated
    private StateConstant useDefaultAmount;
}


Т.е.: Каждый "банк" имеет список "программ страхования на банк".
Каждая "программа страхования  на банк" имеет список "групп рисков в программе страхования на банк".
Каждая "группа рисков в программе страхования на банк" имеет список настроек.
Проблема возникает, когда я настраиваю список "группа рисков в программе страхования на банк": могу добавить новую, могу убрать те что были.
Все сохранения в базе вызываю на коренной сущности - Bank методом EntityManager.merge
Естественно добавление прокатывает нормально: везде проставил соответствующие ИД и произошел INSERT. А вот когда я из списка убираю группу рисков, то вылазит такая беда: 
Код

 update
        BANKINSURANCESCHEMERISK 
    set
        BANKID=null,
        INSURANCESCHEMEID=null 
    where
        BANKID=? 
        and INSURANCESCHEMEID=? 
        and bankId=? 
        and insuranceRiskGroupId=? 
        and insuranceSchemeId=?

 ERROR [JDBCExceptionReporter] ORA-01407: cannot update ("MYSCHEME"."BANKINSURANCESCHEMERISK"."BANKID") to NULL

что для меня не есть логично. Я-то могу убрать констрейнты, но это ж фигня выйдет: полна таблица NULL'ов и никаких удалений.
Когда у меня было простое тернарное соединение без пожелания заказчика добавить к нему настройки было так:
Код

@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name = "BANKINSURANCESCHEMERISK",
       joinColumns = {
               @JoinColumn(name = "BANKID"),
               @JoinColumn(name = "INSURANCESCHEMEID")
       },
       inverseJoinColumns = {
               @JoinColumn(name = "INSURANCERISKGROUPID")
       })
private Set<RiskGroup> riskGroups = new HashSet<RiskGroup>();

Естественно все работало.
Понимаю, что где-то проблема на композитном ключе. Но как ее побороть, блин, что бы при мерже коренной сущности корректно грохались сущности из вложенной коллекции.
Да, еще было такое:
Код

@Entity
@Table(name = "INSURANCEPOLICY")
@SequenceGenerator(name = "SEQUENCE", sequenceName = "GEN_INSURANCEPOLICY", allocationSize = 1)
public class Policy implements Serializable {
    @Id
    @GeneratedValue(generator = "SEQUENCE")
    @Column(name = "ID", updatable = false, nullable = false, unique = true)
    private Long id;
   
   @OneToOne(mappedBy = "policy", cascade = {CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.EAGER)
    private PolicyDocument document;
}
@Entity
@Table(name = "INSURANCEPOLICYDOCUMENT")
@org.hibernate.annotations.AccessType("field")
@SequenceGenerator(name = "SEQUENCE", sequenceName = "GEN_INSURANCEPOLICYDOCUMENT", allocationSize = 1)
public class PolicyDocument implements Serializable {
    @Id
    @GeneratedValue(generator = "SEQUENCE")
    @Column(updatable = false, nullable = false, unique = true)
    private Long id;

    @OneToOne
    @JoinColumn(name = "INSURANCEPOLICYID")
    private Policy policy;
}

Тоже, когда пытался JPA-возможностями воспользоваться:
Код

policy.setDocument(null);

Тоже выдавал подобную фигню в стиле:
Код

update
        INSURANCEPOLICYDOCUMENT
    set
        POLICYID=null

Как быть? помогите


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
Evgeni68
Дата 13.7.2009, 23:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 37
Регистрация: 9.7.2007

Репутация: 1
Всего: 3



Может быть я не до конца понял проблему. При работе с Hibernate пользуюсь только xml-маппингом, так вот удаление сущности из БД при удалении ее из коллекции родительской сущности, позволяет только указание типа каскадности: cascde="all, delete-orphan". Вроде бы есть аннотация CascadeType.DELETE_ORPHAN. Возможно комбинация ALL и DELETE_ORPHAN поможет.

Плюс, непонятно как связаны сущности RiskGroup и BankInsuranceschemeRiskGroup. Если RiskGroup содержит коллекцию BankInsuranceschemeRiskGroup, то с какой стати BankInsuranceschemeRiskGroup должна удалиться.
PM MAIL   Вверх
MisterCleric
Дата 13.7.2009, 23:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1043
Регистрация: 16.2.2006
Где: Харьков, Украина

Репутация: 33
Всего: 38



Цитата

Если RiskGroup содержит коллекцию BankInsuranceschemeRiskGroup, то с какой стати BankInsuranceschemeRiskGroup должна удалиться.

Обязательно должна удалиться! Так как RiskGroup - это справочник. А если мы что-то убираем из спавочника, то все что с ним связанно должно подохнуть. Так как все остальные страховые дела потом идут через нее. Раньше были настройки на уровне этого справочника на всю систему одни настройки на одну группу рисков. а теперь заказчик захотел что бы группа рисков была одна, а настройки были на уровне конкретной программы в конкретном банке
Цитата

Возможно комбинация ALL и DELETE_ORPHAN поможет.

Спасибо. попробую.
Еще мой DBA предложил сделать нормализацию: убрать везде композитные ключи и навешать сиквенсов и форинг кеев. Но это уже будем пробовать завтра. Тут я сто пудово уверен, что получится. Но столько придеться переписывать....


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
Evgeni68
Дата 14.7.2009, 08:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 37
Регистрация: 9.7.2007

Репутация: 1
Всего: 3



Цитата(MisterCleric @  13.7.2009,  23:25 Найти цитируемый пост)
Так как RiskGroup - это справочник. А если мы что-то убираем из спавочника...

Тогда нет вопросов smile 

А по поводу нормализации - мысль правильная. Частенько при таких "сложных" связках Hibernate ведет себя не так, как ожидаешь.
PM MAIL   Вверх
DimW
Дата 14.7.2009, 10:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1330
Регистрация: 24.2.2005
Где: Орёл

Репутация: 3
Всего: 44



Цитата(MisterCleric @  13.7.2009,  17:18 Найти цитируемый пост)
Я-то могу убрать констрейнты, но это ж фигня выйдет: полна таблица NULL'ов и никаких удалений.

MisterCleric, констрайнты тут не причем просто поле BANKID создано как not null. удаление foreign key -ев не решит проблему.

Цитата(MisterCleric @  13.7.2009,  23:25 Найти цитируемый пост)
Обязательно должна удалиться! Так как RiskGroup - это справочник. А если мы что-то убираем из справочника, то все что с ним связанно должно подохнуть.

на мой взгляд не совсем логично. если рыссматривать проблему некоректно введенных данных в справочник то да, удаление необходимо. если рассматривать ситуацию что с какого то  времени позиция в RiskGroup должна прекратить свое действие то правельней будет проставить дату закрытия и предусмотреть это в запросах.

что касается каскадного удаления то это можно предусмотреть при создании foreign key:
Код

alter table MYTABLE
  add constraint MYTABLE_FK foreign key (MYTABLEID)
  references PARENTTABLE (PARENTTABLEID) on delete cascade;

т.е. on delete cascade вам обеспечит каскадное удаление позиций в сущностях при удалении связанной позиции в справочнике(но дата завершения всеравно понятней и логичней и дает возможность заглянуть в прошлоеsmile).

  

Это сообщение отредактировал(а) DimW - 14.7.2009, 10:35
PM MAIL ICQ   Вверх
MisterCleric
Дата 14.7.2009, 11:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1043
Регистрация: 16.2.2006
Где: Харьков, Украина

Репутация: 33
Всего: 38



Цитата

но дата завершения всеравно понятней и логичней

 smile Здесь вопросов нет. Но это, ребята, вы уже начинаете разгребать логику моего приложения, а я задал вполне конкретный вопрос: как победить удаление элементов из коллекции, что бы они потом также удалились из базы, если у меня композитный ключ?
Мне удалять каскадом из настроечной таблицы при удалении пока не надо. Просто надо удалить из таблицы настроек, если я удалю из кллекции этих настроек, а сохраню сущность которая содержит эту коллекцию


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
DimW
Дата 14.7.2009, 11:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1330
Регистрация: 24.2.2005
Где: Орёл

Репутация: 3
Всего: 44



Цитата(MisterCleric @  14.7.2009,  11:00 Найти цитируемый пост)
Просто надо удалить из таблицы настроек, если я удалю из кллекции этих настроек, а сохраню сущность которая содержит эту коллекцию

иными словами вы пытаетесь удалить позицию справочника и оставить на нее ссылку в другой таблице? ссылку на идентификатор которого нет???
PM MAIL ICQ   Вверх
MisterCleric
Дата 14.7.2009, 11:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1043
Регистрация: 16.2.2006
Где: Харьков, Украина

Репутация: 33
Всего: 38



Цитата

на нее ссылку в другой таблице

Такой ссылки нету. Есть сущность "Программа страхования" у нее есть коллекция "Группы рисков". Вот в эту коллекцию я добавляю и убираю из нее элементы одним махом. потом сохраняю эту "Программа страхования". Вложенная коллекция должна выполнить соответствующие операции по инсерту и делиту, а если надо и апдейту - так как пользователь может поменять какие-то настройки в конкретной группе рисков. Вся загвоздка в том, что у меня композитные ключи. В общем я тут что-то понаписывал, счас тестирую: вроде заработало. Будет работать четко опишу, что вышло 


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
DimW
Дата 14.7.2009, 11:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1330
Регистрация: 24.2.2005
Где: Орёл

Репутация: 3
Всего: 44



Цитата(MisterCleric @  14.7.2009,  11:42 Найти цитируемый пост)
Вся загвоздка в том, что у меня композитные ключи.

что мне подсказывает что трабла не в составных ключах.

Цитата(MisterCleric @  14.7.2009,  11:42 Найти цитируемый пост)
Есть сущность "Программа страхования" у нее есть коллекция "Группы рисков". 

"Группы рисков" относятся еще к какой нить сущности?

PM MAIL ICQ   Вверх
MisterCleric
Дата 14.7.2009, 12:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1043
Регистрация: 16.2.2006
Где: Харьков, Украина

Репутация: 33
Всего: 38



В общем, решилось быстро так:
Код

@Entity
@org.hibernate.annotations.AccessType("field")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@IdClass(BankInsuranceSchemePk.class)
public class BankInsuranceScheme implements Serializable {
    @Id
    private Long bankId;

    @Id
    private Long insuranceSchemeId;

    @OneToMany(mappedBy = "bankInsuranceScheme", fetch = FetchType.EAGER, cascade = CascadeType.ALL) // HERE
    @org.hibernate.annotations.Cascade(
            value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN
    )
    @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
    @OptimisticLock(excluded = true)
    @Fetch(FetchMode.SELECT)
    private Set<BankInsuranceschemeRiskGroup> riskGroups = new HashSet<BankInsuranceschemeRiskGroup>();
}
@Entity
@Table(name = "BANKINSURANCESCHEMERISK")
@org.hibernate.annotations.AccessType("field")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@IdClass(BankInsuranceschemeRiskGroupPk.class)
public class BankInsuranceschemeRiskGroup implements Serializable {
    @Id
    private Long bankId;

    @Id
    private Long insuranceSchemeId;

    @Id
    private Long insuranceRiskGroupId;

    @ManyToOne
    @JoinColumns({
            @JoinColumn(name = "BANKID", insertable = false, updatable = false),
            @JoinColumn(name = "INSURANCESCHEMEID", insertable = false, updatable = false)
    })
    private BankInsuranceScheme bankInsuranceScheme;    // AND HERE

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "INSURANCERISKGROUPID", insertable = false, updatable = false) // ALSO HERE
    private RiskGroup riskGroup;

}


Т.е. устроил в сущоности из коллекции ссылку на ту сущность о которой она зависит. А в коренной сущность указал bidirection/ Приходиться правда кроме элементов композитного ключа указывать еще и форинг-сущность при добавлении в коллекцию. 
Естественно выходит, что формируется корректный DELETE FROM:
Код

 delete 
    from
        BANKINSURANCESCHEMERISK 
    where
        bankId=? 
        and insuranceRiskGroupId=? 
        and insuranceSchemeId=?


Так что делаем выводы: для корректной работы EntityManager надо везде где можно проставлять bidirection. Так же помогло DELETE_ORPHAN. Что оно такое почитаю.
А Evgeni68 получает плюсик.
Естественно, в дальнейшем надо сделать нормализацию базы. Но эт в моем случае можно. Так как я архитектор базы. Но есть другие базы, которые уже написаны и надо к ним прикручивать функционал на java. Вот там и помогут знания о каскадном удалении и компзитных ключах. Считаю тему закрытой. Всем спасибо за участие. Но естественно от советов не откажусь


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java EE (J2EE) и Spring | Следующая тема »


 




[ Время генерации скрипта: 0.1075 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.