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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Hibernate. Annotations. CascadeType 
:(
    Опции темы
garbuz
Дата 13.7.2009, 22:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

Репутация: 8
Всего: 11



Использую Hibernate + Annotations. Запутался с cascadeType.

Есть к примеру связь Many-to-One
Код

@Entity
@Table(name = "orders")
public class Order {

...

    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User user;
...
}


Код

@Entity
@Table(name = "users")
public class User {

...

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.LAZY)
    private Set<Order> orders = new HashSet<Order>();
...

}


Правильно ли я указал cascadeType? Я что-то не совсем догнал, что каждый из них значит. Будет ли при удалении юзера удаляться все его ордеры? Если не сложно, может кто в двух словах объяснит, что значит каждый cascade type?

И еще один вопрос.
Имеется класс Message, в нем есть два поля типа User - fromUser и toUser
Код

@Entity
@Table(name = "messages")
public class Message {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "messageId", nullable = false)
    private Long messageId;

    @Column(name = "title", nullable = false)
    private String title;

    @Column(name = "body", nullable = false)
    private String body;

    @Column(name = "date", nullable = false)
    private Date date;

    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User fromUser;

    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User toUser;
    
    ...
}


У класса User есть коллекция мессджей, которые должны делиться на входящие и исходящие.
Код

@Entity
@Table(name = "users")
public class User {

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "fromUser, toUser", fetch = FetchType.LAZY)
    private Set<Message> messages = new HashSet<Message>();    

}


Как правильно указать mappedBy? На что замапить? Сейчас я просто через запятую написал fromUser, toUser, но уверен, что это не сработает. Да, и как тут с cascadeType? При удалении пользователя будут удаляться все его сообщения или нет?
PM MAIL   Вверх
MisterCleric
Дата 13.7.2009, 23:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



Про mappedBy точно могу сказть, что надо тебе две коллекции: входящие и исходящие.

Цитата

 @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User fromUser;
    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User toUser;


Вот это не понятно: а как ты разюираешь от "кого" и "кому", если  у тебя всего лишь один форинг кей - userId

Вот о cascadeType и я бы послушал, так как у меня тоже такие траблы есть. Мое предыдущее сообщение.
Ну если в общих чертах как я понял. То какскад нужен, если у тебя большая вложенность сущностей. Для твоего примера будет где-то так:
У юзера есть мессажды, у месаждей есть файлы. И вот ты добавил несколько файлов к мессажду, а несколько удалил. А merge вызвал на юзере. Вот ты и увидешь несколько селектов, несколько инсертов, а по остальным делиты. Про DELETE FROM, конечно молчу, так получил с этим проблемы...


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


Опытный
**


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

Репутация: 8
Всего: 11



Цитата(MisterCleric @  13.7.2009,  23:16 Найти цитируемый пост)
Про mappedBy точно могу сказть, что надо тебе две коллекции: входящие и исходящие.

Код

 @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User fromUser;
    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User toUser;


Вот это не понятно: а как ты разюираешь от "кого" и "кому", если  у тебя всего лишь один форинг кей - userId

Да что-то и мне пока не совсем понятно :( А одной коллекцией никак не обойтись? Разбирать буду просто. Все сообщения, в которых юзер получатель - входящие, отправитель - отправленные.



Цитата(MisterCleric @  13.7.2009,  23:16 Найти цитируемый пост)
Вот о cascadeType и я бы послушал, так как у меня тоже такие траблы есть. Мое предыдущее сообщение.
Ну если в общих чертах как я понял. То какскад нужен, если у тебя большая вложенность сущностей. Для твоего примера будет где-то так:
У юзера есть мессажды, у месаждей есть файлы. И вот ты добавил несколько файлов к мессажду, а несколько удалил. А merge вызвал на юзере. Вот ты и увидешь несколько селектов, несколько инсертов, а по остальным делиты. Про DELETE FROM, конечно молчу, так получил с этим проблемы... 


Почти ничего не понял smile

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


Опытный
**


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

Репутация: 8
Всего: 11



Сделал две коллекции.

User.java
Код

 @OneToMany(cascade = CascadeType.ALL, mappedBy = "toUser", fetch = FetchType.LAZY)
    private Set<Message> receivedMessages = new HashSet<Message>();

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "fromUser", fetch = FetchType.LAZY)
    private Set<Message> sentMessages = new HashSet<Message>();



Message.java
Код

 @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User fromUser;

    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User toUser;


Хтбернейт ругается 
Цитата

Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: my.diplom.entity.Message column: userId (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:652)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:674)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:696)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:450)
at org.hibernate.mapping.RootClass.validate(RootClass.java:192)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1108)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1293)
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:814)
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:732)
at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:211)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1368)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1334)
... 46 more

Process finished with exit code 1


Если сделать, как предлагается, т.е.
Код

@ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false, insertable = false, updatable = false)
    private User fromUser;

    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false, insertable = false, updatable = false)
    private User toUser;


То все заводится, но смущают меня эти значения insertable = false, updatable = false Это типа значит, что значение этому полю мне не присвоить и не обновить?
PM MAIL   Вверх
MisterCleric
Дата 15.7.2009, 12:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



Цитата

 @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User fromUser;
    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User toUser;

я же тебе говорил, что логика не верна: как ты понимаешь от "кого" "кому", если у тебя только один форинг кей на юзера? Нужно два.

Поясни логику этих вот мессаджей. 
Как я понял, кто-то отправляет кому-то мессадж. Знач мессадж должен знать об этом. А у тебя всего один userId


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


Опытный
**


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

Репутация: 8
Всего: 11



Кто-то отправляет кому-то мессадж, типа как ты в личку пишешь мне, а я тебе smile Есть отправитель и получатель.
Я что-то сам запутался с форинг ключами. В таблице messages два форинг ключа, только ссылаются они на один и тот же первичный ключ таблицы users. Как правильно сделать?
user posted image
PM MAIL   Вверх
MisterCleric
Дата 15.7.2009, 13:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



все правильно. Ничего смертельного, что у тебя два форинг кея ссылаются на один.
У меня есть такое в пиложении:
Банк имеет форинг кей сам на себя, так как банк может филиалом и, естественно, у него есть главным банк
Есть документ заведенный в определенном банке(а может и филиале), а также в этом документе есть форинг кей на главный банк. т.е. где-то так
Код

bankid  NUMBER(10,0) CONSTRAINT fk_insurancepolicy_bankid FOREIGN KEY (bankid) REFERENCES bank (id),

headbankid  NUMBER(10,0) CONSTRAINT fk_insurancepolicy_hbankid FOREIGN KEY (headbankid) REFERENCES bank (id)


и все отлично работает


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


Опытный
**


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

Репутация: 8
Всего: 11



Вот что сгенерила моя тулза, где я БД рисовал
Код

alter table messages add index FKmessages455398 (toUserId), add constraint FKmessages455398 foreign key (toUserId) references users (userId);
alter table messages add index FKmessages410989 (fromUserId), add constraint FKmessages410989 foreign key (fromUserId) references users (userId);

Что не так?
PM MAIL   Вверх
MisterCleric
Дата 15.7.2009, 14:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



Правильно тулза сделала. 
у тебя мепинг неверный:
Цитата

@ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User fromUser;
    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "userId", nullable = false)
    private User toUser;


обратить внимание на userId в тексте!


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


Опытный
**


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

Репутация: 8
Всего: 11



MisterCleric, я видимо чего-то не понимаю, раз мне ошибку не увидеть. Если можешь, объясни пожалуйста.
PM MAIL   Вверх
MisterCleric
Дата 15.7.2009, 14:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



Может и я что-то не понимаю. Но как мне кажеться:
1. Ты пишешь, что у тебя есть в таблице messages две колонки: toUserId, fromUserId
2. Ты меппишь сущность Message на эту таблицу. Но при этом не меппишь эти колонки.
3. Ты все указал правильно в @ManyToOne, но только у каждого User  в аннотации @JoinColumn должен быть указан не примари кей юзера, а его соответствующий форин кей из таблицы messages


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


Опытный
**


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

Репутация: 8
Всего: 11



MisterCleric,  smile  smile Спасибо! Вреде бы все завелось!
Код

 @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "fromUserId", nullable = false)
    private User fromUser;

    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name = "toUserId", nullable = false)
    private User toUser;
 Я почему-то думал, что надо указывать имя праймари кей, а не форинг кей.
PM MAIL   Вверх
garbuz
Дата 16.7.2009, 15:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

Репутация: 8
Всего: 11



Едем дальше. Опять непонятки у меня с каскадностью.
Есть юнит тест.
Код

 @Test
    @Rollback(false)
    public void applicationTest() {

        Application application = new Application();
        application.setApplicationName("Ms Office 2003");
        application.setStatus(statusService.getByName("active"));
        application.setCategory(categoryService.getByName("Office"));
        applicationService.create(application);


        Application application2 = new Application();
        application2.setApplicationName("Open Office");
        application2.setStatus(statusService.getByName("active"));
        application2.setCategory(categoryService.getByName("Office"));
        applicationService.create(application2);

        Application application3 = new Application();
        application3.setApplicationName("KOffice");
        application3.setStatus(statusService.getByName("active"));
        application3.setCategory(categoryService.getByName("Office"));
        applicationService.create(application3);

        Assert.assertTrue(categoryService.getByName("Office").getApplications().size() == 3);

        Application application4 = new Application();
        application4.setApplicationName("IDEA");
        application4.setStatus(statusService.getByName("active"));
        application4.setCategory(categoryService.getByName("IDE"));
        applicationService.create(application4);


        Application application5 = new Application();
        application5.setApplicationName("Netbeans");
        application5.setStatus(statusService.getByName("active"));
        application5.setCategory(categoryService.getByName("IDE"));
        applicationService.create(application5);

        int size1 = categoryService.getByName("Office").getApplications().size();
        System.out.println("size1 = " + size1);
        Assert.assertTrue(categoryService.getByName("Office").getApplications().size() == 3);
        applicationService.delete(application3);
        int size2 = categoryService.getByName("Office").getApplications().size();
        for (Iterator<Application> it = categoryService.getByName("Office").getApplications().iterator(); it.hasNext();){
            Application a = it.next();
            System.out.println("app name = " + a.getApplicationName());
        }
        System.out.println("size2 = " + size2);
        Assert.assertTrue(categoryService.getByName("Office").getApplications().size() == 2); // валится тут!!!


        application2.setApplicationName("Open Ofiice 3.4");
        applicationService.update(application2);

        categoryService.delete(categoryService.getByName("Office"));


    }


Вот что выводит консоль
Цитата

16:23:44,328 DEBUG main StatusDAOImpl:getByName:47 - get status by status name = active
Hibernate: select status0_.statusId as statusId9_, status0_.statusName as statusName9_, status0_.alias as alias9_ from statuses status0_ where status0_.statusName=?
16:23:44,546 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
16:23:44,562 DEBUG main ApplicationDAOImpl:create:29 - create applicationMs Office 2003
Hibernate: insert into applications (applicationName, description, statusId, categoryId) values (?, ?, ?, ?)
16:23:44,578 DEBUG main StatusDAOImpl:getByName:47 - get status by status name = active
Hibernate: select status0_.statusId as statusId9_, status0_.statusName as statusName9_, status0_.alias as alias9_ from statuses status0_ where status0_.statusName=?
16:23:44,593 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
16:23:44,593 DEBUG main ApplicationDAOImpl:create:29 - create applicationOpen Office
Hibernate: insert into applications (applicationName, description, statusId, categoryId) values (?, ?, ?, ?)
16:23:44,593 DEBUG main StatusDAOImpl:getByName:47 - get status by status name = active
Hibernate: select status0_.statusId as statusId9_, status0_.statusName as statusName9_, status0_.alias as alias9_ from statuses status0_ where status0_.statusName=?
16:23:44,609 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
16:23:44,609 DEBUG main ApplicationDAOImpl:create:29 - create applicationKOffice
Hibernate: insert into applications (applicationName, description, statusId, categoryId) values (?, ?, ?, ?)
16:23:44,609 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
Hibernate: select applicatio0_.categoryId as categoryId1_, applicatio0_.applicationId as applicat1_1_, applicatio0_.applicationId as applicat1_0_0_, applicatio0_.applicationName as applicat2_0_0_, applicatio0_.description as descript3_0_0_, applicatio0_.statusId as statusId0_0_, applicatio0_.categoryId as categoryId0_0_ from applications applicatio0_ where applicatio0_.categoryId=?
16:23:44,625 DEBUG main StatusDAOImpl:getByName:47 - get status by status name = active
Hibernate: select status0_.statusId as statusId9_, status0_.statusName as statusName9_, status0_.alias as alias9_ from statuses status0_ where status0_.statusName=?
16:23:44,625 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = IDE
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
16:23:44,625 DEBUG main ApplicationDAOImpl:create:29 - create applicationIDEA
Hibernate: insert into applications (applicationName, description, statusId, categoryId) values (?, ?, ?, ?)
16:23:44,640 DEBUG main StatusDAOImpl:getByName:47 - get status by status name = active
Hibernate: select status0_.statusId as statusId9_, status0_.statusName as statusName9_, status0_.alias as alias9_ from statuses status0_ where status0_.statusName=?
16:23:44,640 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = IDE
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
16:23:44,640 DEBUG main ApplicationDAOImpl:create:29 - create applicationNetbeans
Hibernate: insert into applications (applicationName, description, statusId, categoryId) values (?, ?, ?, ?)
16:23:44,656 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
size1 = 3
16:23:44,656 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
16:23:44,656 DEBUG main ApplicationDAOImpl:delete:56 - delete applicationKOffice
Hibernate: delete from applications where applicationId=?
16:23:44,671 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
16:23:44,671 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?
app name = Open Office
app name = Ms Office 2003
app name = KOffice
size2 = 3
16:23:44,687 DEBUG main CategoryDAOImpl:getByName:51 - get category by name = Office
Hibernate: select category0_.categoryId as categoryId1_, category0_.categoryName as category2_1_, category0_.description as descript3_1_, category0_.statusId as statusId1_ from appCategories category0_ where category0_.categoryName=?

junit.framework.AssertionFailedError: null
    at CategoryAndApplicationTest.applicationTest(CategoryAndApplicationTest.java:136)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:163)
    at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
    at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
    at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
    at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
    at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:142)
    at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
    at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
    at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
    at com.intellij.rt.junit4.Junit4ClassSuite.run(Junit4ClassSuite.java:99)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)



Как видно из лога, хибернейт удаляет application
Цитата

16:23:44,656 DEBUG main ApplicationDAOImpl:delete:56 - delete applicationKOffice
Hibernate: delete from applications where applicationId=?

Однако, если после удаления, мы у категории проитерируем коллекцию приложений, то мы все равно увидим размер коллеции 3, хотя должно быть 2

Вот маппинг у классов

Category.java
Код

@Entity
@Table(name = "appCategories")
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "categoryId", nullable = false)
    private Long categoryId;

    @Column(name = "categoryName", nullable = false)
    private String categoryName;

    @Column(name = "description", nullable = true)
    private String description;


    @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE},
            mappedBy = "category", fetch = FetchType.LAZY)
    private Set<Application> applications = new HashSet<Application>();


Application.java
Код

@Entity
@Table(name = "applications")
public class Application {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "applicationId", nullable = false)
    private Long applicationId;

    @Column(name = "applicationName", nullable = false)
    private String applicationName;

    @Column(name = "description", nullable = true)
    private String description;

   
    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
    @JoinColumn(name="categoryId", nullable = false)
    private Category category;

PM MAIL   Вверх
garbuz
Дата 16.7.2009, 16:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

Репутация: 8
Всего: 11



Если удалять не так
Код

applicationService.delete(application3);


А вот так
Код

 categoryService.getByName("Office").getApplications().remove(application3);

Тогда все нормально. Ерунда какая-то получается. Т.е. если мы у родительской коллекции удаляем елемент, то он удаляется, а если удаляем сам отдельно элемент, то он остается. Странно.
PM MAIL   Вверх
MisterCleric
Дата 20.7.2009, 14:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



Привет еще раз.
Тут ситуация в том, что в Persistence есть такое понятие как managed & detached. Т.е. сущности, которые поцулены из базы и находятся сейчас в памяти - PersistenceContext (еще его называют кешем первого уровня). detached - те сущности, которые созданы в этом PersistenceContext, но еще не лежат в базе(а может и лежат, но PersistenceContext еще об этом не знает).
Так вот. согласно твоему примеру. У тебя не то что, может а как раз так и есть, что сущность Category заменеджена и имеет у себя внутри коллекцию applications один из элементов которой как раз и есть тот объект, который ты хочешь грохнуть.
Ты ему говоришь куском кода:
Цитата

applicationService.delete(application3);


удали-ка такой-то объект из базы и PersistenceContext. Он так и сделает, но ссылка на него в коллекции-то останется... Ты же тут не сказал что ее надо убрать и из коллекции. А вот второй кусок:
Цитата

categoryService.getByName("Office").getApplications().remove(application3);

Работает правильно, так как эта коллекция есть запроксированная PesistanceSet, т.е. все изменения в этой коллекции будут отделегированы PersistenceContext для выполнения соответствующих изменений: CRUD-операций.

Делаем выводы: первый кусок тоже отработает. Но надо или в отдельной транзакции его удалять, а уже потом заправшивать в другой транзакции родительскую сущность. Или надо руками из коллекции удалить, а потом вызвать удаление из базы.
вроде так...


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


Опытный
**


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

Репутация: 8
Всего: 11



MisterCleric, после нескольких перечитываний разобрался, в том, что ты хотел до меня донести - недельный отдых дает о себе знать, мозг еще не включился smile
Итак по делу.

А допустим у меня есть несколько сущностей, который содержат коллекцию пользователей. Хочу я удалить одного пользователя из базы. Мне придется сперва из всех этих коллекций удалить пользователя, а потом уже удалить его из базы? Ерунда какая-то получается. 

MisterCleric,  что скажешь про orphan deletion? Я что-то почитал, но если честно что-то не очень понял.

Встает такой вопрос. Если столько проблем с этими коллекциями, удалением элементов etc. Надо ли вообще эти коллекции?
В чем удобство? 
Код

category.getApplications()
. Да согласен. Но я так же могу написать простой запрос
Код

from Application a where a.category = ?

Сразу отпадает проблема с ленивыми коллекциями и удалением элементов. Удалил 
Код

applicationService.delete(application)

И нигде больше не вылезет этот application. Или я не прав?

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


Эксперт
***


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

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



Привет. Слушай, давай ты почитаешь
Java Persistence with Hibernate
страница 273. А потом попытаемся разобраться. 
Вряд ли я смогу своими словами более толково объяснить, чем там написано.
Все ты рассуждаешь правильно. И про коллекции и про ленивость, но у меня одно замечание:
Зачем так наворачивать  в одном PersistenceContext такую логику, что у тебя будет куча сущностей с коллекциями OneToMany да еще так, что придется убивать одну из сущностей, что может быть в этих коллекциях?

Последний твой пример абсолютно верный: но он естественно, будет работать верно, если ты ленивую коллекцию позовешь после удаления. Да еще, по-моему, придется перед вызовом ленивой коллекции или сразу после удаления вызвать
Код

em.flush();


для насильного вызова всех предыдущих перзистенс-операций. Так как INSERT/DELETE/UPDATE по-умолчанию планируются для вызова при коммите транзакции.



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


Опытный
**


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

Репутация: 8
Всего: 11



Цитата(MisterCleric @  27.7.2009,  10:41 Найти цитируемый пост)
Зачем так наворачивать  в одном PersistenceContext такую логику, что у тебя будет куча сущностей с коллекциями OneToMany да еще так, что придется убивать одну из сущностей, что может быть в этих коллекциях?

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

Цитата(MisterCleric @  27.7.2009,  10:41 Найти цитируемый пост)
еще, по-моему, придется перед вызовом ленивой коллекции или сразу после удаления вызвать
Выделить всёкод Java
    
Код

em.flush();


для насильного вызова всех предыдущих перзистенс-операций. Так как INSERT/DELETE/UPDATE по-умолчанию планируются для вызова при коммите транзакции.

Ну наверно так и буду делать.
Код

hibernateTemplate.flush();


Получится, что ассоциации у меня будут не bidirectional, а undirectional.
Цитата из книги
Цитата

Always try to think of these kinds of collection mappings as a feature, not a requirement. If it gets too difficult, don’t do it.)

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


Эксперт
***


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

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



Цитата

Ну видимо логика не очень удачная получилась.

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

Получится, что ассоциации у меня будут не bidirectional, а undirectional
.
Не всегда это плохо. У меня тут по каскадному удалению была тема. И как раз bidirectional выручил


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


Опытный
**


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

Репутация: 8
Всего: 11



Да, чувствуется, что не хватает теоритических знаний :(
Насколько я понимаю, PersistenceContext - это область памяти, где хранятся мои объекты в рамках одной транзакциb или сессии. Верно? Если так, то естественно в одном контексте не будет использоваться так много сущностей. Я понял, что ты хотел сказать.
Ладно, вечером попробую поэксперементировать с тестами. Еще отпишу чего-нить smile Еще раз спасибо за помошь!

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


Эксперт
***


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

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



Цитата

Если так, то естественно в одном контексте не будет использоваться так много сущностей.

Да ты знаешь: не всегда. У меня получилась такая логика в приложении, что уже не рад: банк, программы страхования, группы рисков, тарифы, опросники, ответы на опросы, полис, физ. лицо. 
И иногда получалось такое несуразное, что мама не горюй: так оно не укладывалось в голове. В одной транзакции удалял ответ из коллекции - было все ОК(Это логика анкеты). В другой транзакции по логике "невидимых вопрос" вызвал тот же метод удаления ответа из коллекции получил эксепшн, что-то типа in detached state. В общем надо как-то приводить логику своего приложения к какому-то нормализованному виду, как это делается в базе введение примари кеев одним полем. Но на это все надо времени, естественно...  


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "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.0890 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


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

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