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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> История Entity 
V
    Опции темы
ShurikA
Дата 6.1.2009, 12:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Мне нужно сохранять историю изменений Entities в таблицу истории.
Пытаюсь сделать это через EntityListener - @PreUpdate, вроде по логике должно быть именно так.
Но, дело в том что из нутри Entity нету инджекшен @PersistanceContext.
Какое правильное решение для такого дела?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 6.1.2009, 13:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  6.1.2009,  12:39 Найти цитируемый пост)
Мне нужно сохранять историю изменений Entities в таблицу истории.
Пытаюсь сделать это через EntityListener - @PreUpdate, вроде по логике должно быть именно так.
Но, дело в том что из нутри Entity нету инджекшен @PersistanceContext.
Какое правильное решение для такого дела? 


Для ведения истории обычно entity имеет поле для версии и из БД их просто не удаляют.



--------------------
user posted image нет времени думать - нужно писать КОД!

PM MAIL   Вверх
ShurikA
Дата 6.1.2009, 13:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  6.1.2009,  12:35 Найти цитируемый пост)
Для ведения истории обычно entity имеет поле для версии и из БД их просто не удаляют.

Я уже думал об этом, но нужно именно копировать в другую таблицу.

Добавлено через 2 минуты и 2 секунды
powerOn
Кстати, а если сделать так как ты сказал. То каким образом задавать EntityManager-у условие подбора правильного Entity?

Добавлено через 3 минуты и 1 секунду
Цитата(powerOn @  6.1.2009,  12:35 Найти цитируемый пост)
Для ведения истории обычно entity имеет поле для версии и из БД их просто не удаляют.

И ещё: как бы это ни было, мне нужен @PersistanceContext.


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 6.1.2009, 14:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  6.1.2009,  13:45 Найти цитируемый пост)
То каким образом задавать EntityManager-у условие подбора правильного Entity?


Через запрос в котором так же будет указана версия объекта. Вообще тут нужно конкретный use case рассмотреть. Возможно, какое-нибудь вообще другое решение подойдет... 

Цитата(ShurikA @  6.1.2009,  13:45 Найти цитируемый пост)
И ещё: как бы это ни было, мне нужен @PersistanceContext. 

В Entity классе его автоматически не получить, поскольку они не управляются контейнером. Эти объекты ты создаешь сам, в отличии от EJB, которые для тебя создал и проинициализировал контейнер EJB. Вот контейнер создавая EJB позаботился о том, чтоб ссылка на EntityManager была поставленна корректно, а ты создавая свои entity нет. ;-) 
Получается что выход тут один (вернее два): 
1) самостоятельно передавать ссылку на EntityManager в слушателя сущностей. Замечу, что разметить аннотациями @PreUpdate и др. можно не только саму сущность, но и другой класс, подключив его в последствии к сущности как слушателя через аннотацию @EntityListeners.
2) Сделать так что бы слушатель сущностей сам находил подходящего EntityManager-а. Например через JNDI lookup.




--------------------
user posted image нет времени думать - нужно писать КОД!

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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  6.1.2009,  13:15 Найти цитируемый пост)
Замечу, что разметить аннотациями @PreUpdate и др. можно не только саму сущность, но и другой класс, подключив его в последствии к сущности как слушателя через аннотацию @EntityListeners.

А это интересно... Может ли быть EJB слушателем?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 6.1.2009, 14:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  6.1.2009,  14:22 Найти цитируемый пост)
Может ли быть EJB слушателем? 

Как простой класс может, но его создавать будет не контейнер EJB, а значит и инжектировать EntityManager не получится... Оно и понятно, ведь в JavaSE приложениях тоже можно использовать JPA и создавать @EntityListeners, а там то не никакого EJB контейнера, все на плечи JPA имплементации положено...


--------------------
user posted image нет времени думать - нужно писать КОД!

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


Эксперт
***


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

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



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


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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



У меня такая  идея:
Сделать Message Driven Bean который всего лиш будет принимать обьект Entity, причём не важно какой, и загонять его в базу.
При этом, сам Entity, которому нужна история, будет создавать копию исторического Entity для самого себя, и отправлять в Bean.

Добавлено через 1 минуту и 13 секунд
Цитата(ShurikA @  6.1.2009,  12:45 Найти цитируемый пост)
powerOn, 
Кстати, а если сделать так как ты сказал. То каким образом задавать EntityManager-у условие подбора правильного Entity?

Есть что то такое?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 7.1.2009, 10:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  6.1.2009,  12:35 Найти цитируемый пост)
Для ведения истории обычно entity имеет поле для версии и из БД их просто не удаляют.

Всё таки всё перекрутил и решол делать как ты предложил.
Получилось следующее:
Код

public class CustomerTypeId implements Serializable {

    private Integer id;
    private Date effectiveDate;

    public CustomerTypeId() {
    }

    public CustomerTypeId(Integer id, Date effectiveDate) {
        this.id = id;
        this.effectiveDate = effectiveDate;
    }

    public Integer getId() {
        return this.id;
    }

    public Date getEffectiveDate() {
        return this.effectiveDate;
    }

    @Override
    public boolean equals(Object obj) {
        return ((obj instanceof CustomerTypeId) && 
                ((CustomerTypeId) obj).getId().equals(this.id) &&
                ((CustomerTypeId) obj).getEffectiveDate().equals(this.effectiveDate));
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + (this.id != null ? this.id.hashCode() : 0);
        hash = 97 * hash + (this.effectiveDate != null ? this.effectiveDate.hashCode() : 0);
        return hash;
    }
}

Код

@Stateless
public class CustomerTypeBean implements CustomerTypeRemote {
    @PersistenceContext(unitName="CRM-ejbPU")
    EntityManager em;
    @Resource
    SessionContext ctx;

    /**
     * Creates new customer type
     * @param type
     */
    public void createCustomerType(String type) {
        CustomerType typeObj = new CustomerType();
        typeObj.setType(type);
        typeObj.setOperator(ctx.getCallerPrincipal().getName());
        em.persist(typeObj);
    }
    /**
     * Gets a full list of available customer types
     */
    public List<CustomerType> getAllCustomerTypes() {
        List<CustomerType> availableCustomerTypes = em.createNamedQuery("CustomerType.findAll").getResultList();
        return availableCustomerTypes;
    }
    /**
     * Updates specific Customer type
     * @param id
     * @param newTypeValue
     */
    public void updateCustomerType(int id, String newTypeValue) {
        //find the type first
        CustomerType typeObj = em.find(CustomerType.class, new CustomerTypeId(id, new Date()));
        if(typeObj != null){
            //update
            typeObj.setType(newTypeValue);
        }
    }
}


но сразу появились пару вопросов:
1. В моём случае я не могу пользоваться точьной датой. Так что получается что вот это
Код

 em.find(CustomerType.class, new CustomerTypeId(id, new Date()));

не сработает.
2. Каким образом мапается этот Entity к Customer::type?

На первый вопрос я вроде как могу ответить сказав что так как персистер пользуется методом equals(Object obj)  что бы найти подходящий ключ, то это метод можно просто изменить так как нам нужно (дата должна быть не равна а между effective и expiration) и спокойно пользоваться em.find(CustomerType.class, new CustomerTypeId(id, new Date()));. Исправь если я не прав.

А вот на тему второго у меня даже догадок никаких.


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 7.1.2009, 16:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Что-то я плохо вопросы понял... Что сделать-то хочешь? Составной ключ (из числа и даты) и поиск по нему?

Цитата(ShurikA @  7.1.2009,  10:11 Найти цитируемый пост)
2. Каким образом мапается этот Entity к Customer::type?


Если Customer::type это как раз и есть составной ключ, то обычно его сущностью мапят с помощью @IdClass аннотации. 




--------------------
user posted image нет времени думать - нужно писать КОД!

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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



powerOn

Дело не в этом.
Когда составной ключь это 2 цифры которые должны совпадать, то всё понятно.
В моём случае:
id - должен совпадать, а дата должна быть >= effectiveDate.

Это сообщение отредактировал(а) ShurikA - 7.1.2009, 21:19


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 7.1.2009, 22:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  7.1.2009,  21:16 Найти цитируемый пост)
id - должен совпадать, а дата должна быть >= effectiveDate.


Что мешает на JPAQuery запрос написать?


--------------------
user posted image нет времени думать - нужно писать КОД!

PM MAIL   Вверх
ShurikA
Дата 7.1.2009, 23:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  7.1.2009,  21:02 Найти цитируемый пост)
Что мешает на JPAQuery запрос написать? 

Абсолютно ничего, тогда когда я хочу прото найти определённый обьект.

А теперь преставь себе что есть Customer у которого есть relationship (соответственно ManyToOne со стороны Customer, так как нужен только последний effective CustomerType)

В таком случае Customer должен выглядеть примерно вот так:
Код

@Entity
public class Customer implements Serializable {
    ...
    @ManyToOne
    @JoinTable(
        name="CustomerType",
        @JoinColumns({
            @JoinColumn(name="customerTypeId", referencedColumnName="id")
            //Как быть с effective date?
        })
    )
    private CustomerType custType;
    ...
}


Как ты понимаешь, если делать mapping только по одной колонке, то вряд ли мне принесётся effectiveCustomerType.




--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 8.1.2009, 09:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Есть ли в TopLink что то вроде @Where, как в Hibernate?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 8.1.2009, 11:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  8.1.2009,  09:54 Найти цитируемый пост)
Есть ли в TopLink что то вроде @Where, как в Hibernate?


Нет, Topllink реализует спецификацию JPA. Все что в спеке есть, все реализовано в Toplink (кроме Table per Concrete Class, ЕМНИП).

Хотелось бы для начала понять отношение сущностей в твоей доменной модели... что нужно в результате получить... может тогда какое решение и придумается...
 


--------------------
user posted image нет времени думать - нужно писать КОД!

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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



OK, 
Есть два Еntity. (см. Picture 1.png). Customer упрощён.
У Customer дольжен содержаться его тип, но только последний (я его назвал effectiveCustomerType), а не вся цепочка.

effectiveCustomerType должен определяться таким образом:
1. Customer.customerTypeId == CustomerType.id
2. CustomerType.effectiveDate <= (настоящее время)
3. CustomerType.expirationDate == null; или > (настоящее время)

Добавлено через 1 минуту и 6 секунд
кстати, CustomerType.effectiveDate тоже PK.

Присоединённый файл ( Кол-во скачиваний: 5 )
Присоединённый файл  Picture_1.png 17,97 Kb


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
MisterCleric
Дата 8.1.2009, 11:56 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



И опять я вмешаюсь в вашу дискуссию. 
У меня что-то подобное есть, так опять таки такая задача решена на уровне базы.
Все сущности хранятся в одной таблицы. Версии хранятся по принципу следующий-предыдущий + поле даты, когда была изменена сущность.
Соответственно первая версия не имеет предшественника (форин кей на предудую версию), последняя версия не имеет "последовательника"
(форин кей на следующую версию).

Зачем что-то выдумывать в java на уровне ORM, если можно по простому на уровне базы, а в java просто реализовать логику реализации этой версионности 


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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(MisterCleric @  8.1.2009,  10:56 Найти цитируемый пост)
Зачем что-то выдумывать в java на уровне ORM, если можно по простому на уровне базы, а в java просто реализовать логику реализации этой версионности  

Что бы по минимому писать SQL запросы, и по максимому пользоваться тем что даёт mapping в JPA.

Добавлено через 2 минуты и 23 секунды
Более того, каким образом в моделе которую ты предлогаешь, будет сделан Mapping сущностей? smile
Проблемма то таже. И она никак не в дата модели...


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 8.1.2009, 12:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(MisterCleric @  8.1.2009,  11:56 Найти цитируемый пост)
Зачем что-то выдумывать в java на уровне ORM, если можно по простому на уровне базы, а в java просто реализовать логику реализации этой версионности  


Если переносимость не важна, то можно и такие варианты рассмотреть. Правда процесс установки приложения будет немного сложнее за счет дополнительно конфигурации БД.

Добавлено через 11 минут и 8 секунд
Цитата(ShurikA @  8.1.2009,  11:47 Найти цитируемый пост)
У Customer дольжен содержаться его тип, но только последний (я его назвал effectiveCustomerType), а не вся цепочка.

А что за цепочка?
Я так понимаю, что каждый CustomerType должен быть связан с неким Customer, но при этом, каждый Customer имеет ссылку на эффективый, т.е. удовлетворяющий неким условиям, CustomerType?



--------------------
user posted image нет времени думать - нужно писать КОД!

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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Так условия вот такие:

Цитата(ShurikA @  8.1.2009,  10:47 Найти цитируемый пост)
1. Customer.customerTypeId == CustomerType.id
2. CustomerType.effectiveDate <= (настоящее время)
3. CustomerType.expirationDate == null; или > (настоящее время)

 И моя проблемма в том что мне нужно базируясь на настоящую дату пределить эти условия, так как на пример CustomerType.effectiveDate никогда уже не будет равен настоящей дате.

Ну или опять же нужно найти способ получить последнюю версию, то есть так или иначе нужна проверка этой самой версии.

Добавлено через 1 минуту и 15 секунд
Как таковой однозначной ссылки на CuatomerType у Customer нету.


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 8.1.2009, 12:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



В ресультате таблица ЦustomerType может выглядеть так:


Присоединённый файл ( Кол-во скачиваний: 9 )
Присоединённый файл  Picture_2.png 19,85 Kb


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 8.1.2009, 12:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Мне кажется нужно делать поиск CustomerType запросом. Без лишних мапингов. Просто пока не вижу причин извращаться.


--------------------
user posted image нет времени думать - нужно писать КОД!

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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  8.1.2009,  11:41 Найти цитируемый пост)
Просто пока не вижу причин извращаться. 

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

Добавлено через 49 секунд
Ну или может ещё какие идеи будут?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 8.1.2009, 13:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Или ещё проще:
Вытаскивать все типы относящиеся к Customer, а логикой уже выберать тот который нужно. Но опять же это трата.


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 8.1.2009, 22:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Нашёл решение.
Поделюсь как только отлажу.


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 8.1.2009, 22:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  8.1.2009,  22:15 Найти цитируемый пост)
Нашёл решение.
Поделюсь как только отлажу. 


Отлично. Выкладывай. smile


--------------------
user posted image нет времени думать - нужно писать КОД!

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


Эксперт
***


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

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



Цитата

Отлично. Выкладывай. smile 


ага, мне тоже интересно...


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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Значит так. 
Я расскажу весь свой ход мыслей и к хему я пришёл в резултате.

Не буду углубляться в саму проблемму (цм. выше), но суть в том что нужно организовать сохранение истории той или иной сущности при этом оставляя возможность relationship mapping (или поиск) по id сущностий

Базовая требуемая сущность такова:
Код

CREATE
    TABLE CustomerType
    (
        id INT(10) NOT NULL AUTO_INCREMENT,
        type VARCHAR(50) NOT NULL,
        active TINYINT(1) DEFAULT '1' NOT NULL,
        effectiveDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        expirationDate TIMESTAMP DEFAULT '0000-00-00 00:00:00' NULL,
        operator VARCHAR(50) NOT NULL,
        PRIMARY KEY USING BTREE (id)
    )


В JPA существует такая вещь как Inheritance при этом нескольких типов:
- TABLE_PER_CLASS
- JOINED
- SINGLE_TABLE

JOINED
Вариант с JOINED я даже не буду разьяснять, так как модель молухается уж безсмысленно сложная и медленная.

SINGLE_TABLE
SINGLE_TABLE выглядет самый экономный и быстрый способ.
для этого нужно добавить ещё одны колонку hist TINYINT(1) хто бы воспользоваться ей как discriminator по типу integer.
создать два класса:
Код

@Entity
@Inheritance(strategy=SINGLE_TABLE)
@DiscriminatorColumn(name="hist", discriminatorType=INT, length=1)
@DiscriminatorValue("0")
public class CustomerType implements Serializable{
....
}


Код

@Entity
@DiscriminatorValue("1")
public class CustomerTypeHist extends CustomerType {
....
}

В классе CustomerTypeHist @Id должен задаваться а не генерироваться, потому что id CustomerTypeHist должен оставаться таким же как и в CustomerType, что бы Customer мог их всех найти. (! проблемма номер раз, нужно превратить ключ в Composit, добавив в него на пример effectiveDate )
А если такое дело ты нихего мы этим не добились и никак не воспользоваться тем что даёт JPA, а нужно гонять запрос ручками.
Не хотим!!!

Остаётся TABLE_PER_CLASS
Во вот это то что нам нужно!!! делаем две таблисы:
Код

CREATE
    TABLE CustomerType
    (
        id INT(10) NOT NULL AUTO_INCREMENT,
        type VARCHAR(50) NOT NULL,
        active TINYINT(1) DEFAULT '1' NOT NULL,
        effectiveDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        expirationDate TIMESTAMP DEFAULT '0000-00-00 00:00:00' NULL,
        operator VARCHAR(50) NOT NULL,
        PRIMARY KEY USING BTREE (id, effectiveDate)
    );

CREATE
    TABLE CustomerTypeHist
    (
        id INT(10) NOT NULL,
        type VARCHAR(50) NOT NULL,
        active TINYINT(1) NOT NULL,
        effectiveDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        expirationDate TIMESTAMP DEFAULT '0000-00-00 00:00:00',
        operator VARCHAR(50) NOT NULL
        PRIMARY KEY USING BTREE (id, effectiveDate)
    );


Два класса:
Код

@Entity
@Inheritance(strategy=TABLE_PER_CLASS)
public class CustomerType implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "id", nullable = false, insertable = true)
    protected Integer id;
    @Basic(optional = false)
    @Column(name = "type", nullable = false, length = 50)
    protected String type;
    @Basic(optional = false)
    @Column(name = "active", nullable = false)
    protected boolean active;
    @Basic(optional = false)
    @Column(name = "effectiveDate", nullable = false, insertable = true)
    @Temporal(TemporalType.TIMESTAMP)
    protected Date effectiveDate;
    @Basic(optional = true)
    @Column(name = "expirationDate", nullable = true, insertable = true, updatable = true)
    @Temporal(TemporalType.TIMESTAMP)
    protected Date expirationDate;
    @Basic(optional = false)
    @Column(name = "operator")
    protected String operator;
...
}


Код

@Entity
@IdClass(CustomerTypeHistId.class)
public class CustomerTypeHist extends CustomerType {

    private static final long serialVersionUID = 1L;
   //добавим ключ
   @Id
   @Basic(optional = false)
   @Column(name = "effectiveDate", nullable = false, insertable = true)
   @Temporal(TemporalType.TIMESTAMP)
   protected Date effectiveDate;
...
}


Теперь Customer может смотреть на CustomerType у которого только одна возможная копия для него, а с историей мы уж как то разберёмся.

Вроде как всё, но не тут то было: TABLE_PER_CLASS не поддержинается в TopLink!!! дасадно!

*Кстати этот вариант я не проверял, так как работаю с TopLink. Если кто проверит расскажите. smile

Ничего из вешеописанных не приблежается к решению.

Но есть ещё вариант: нам не обязательно брать Inheritance от Entity.

Делаем следующее:
Таблицы:
Код

CREATE
    TABLE CustomerType
    (
        id INT(10) NOT NULL AUTO_INCREMENT,
        type VARCHAR(50) NOT NULL,
        active TINYINT(1) DEFAULT '1' NOT NULL,
        effectiveDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        expirationDate TIMESTAMP DEFAULT '0000-00-00 00:00:00' NULL,
        operator VARCHAR(50) NOT NULL,
        PRIMARY KEY USING BTREE (id)
    );

CREATE
    TABLE CustomerTypeHist
    (
        recId INT(10) NOT NULL AUTO_INCREMENT,
        id INT(10) NOT NULL,
        type VARCHAR(50) NOT NULL,
        active TINYINT(1) NOT NULL,
        effectiveDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        expirationDate TIMESTAMP DEFAULT '0000-00-00 00:00:00',
        operator VARCHAR(50) NOT NULL,
        PRIMARY KEY USING BTREE (recId)
    );


И на этот раз 3 класса:
Код

@MappedSuperclass
public class CustomerTypeSuper implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "id", nullable = false, insertable = true)
    protected Integer id;
    @Basic(optional = false)
    @Column(name = "type", nullable = false, length = 50)
    protected String type;
    @Basic(optional = false)
    @Column(name = "active", nullable = false)
    protected boolean active;
    @Basic(optional = false)
    @Column(name = "effectiveDate", nullable = false, insertable = true)
    @Temporal(TemporalType.TIMESTAMP)
    protected Date effectiveDate;
    @Basic(optional = true)
    @Column(name = "expirationDate", nullable = true, insertable = true, updatable = true)
    @Temporal(TemporalType.TIMESTAMP)
    protected Date expirationDate;
    @Basic(optional = false)
    @Column(name = "operator")
    protected String operator;

...
}


Код

@Entity
@Table(name = "CustomerType")
@NamedQueries({
    @NamedQuery(name = "CustomerType.findAll", query = "SELECT c FROM CustomerType c")
})
public class CustomerType extends CustomerTypeSuper {

    private static final long serialVersionUID = 1L;

    public CustomerType() {
    }

...
}


Код

@Entity
@Table(name = "CustomerTypeHist")
@AttributeOverride(name = "id", column = @Column(name = "recId"))
public class CustomerTypeHist extends CustomerTypeSuper {

    private static final long serialVersionUID = 2L;
    @Column(name = "id")
    private Integer regId;

    public CustomerTypeHist() {
    }

    public Integer getRecId() {
        return regId;
    }

    public void setRecId(Integer recId) {
        this.regId = recId;
    }
...
}


Дело в том что на этот раз мы берём только общую структуру у CustomerTypeSuper, а на таблицы завязываем уже сами сущности, которые друг с другом связаны только косвенно.
в таблице CustomerType остаются по одной копии на каждый id тыпа (с одним PK).
CustomerTypeHist recId превращается в PK, a по id мы можем найти всю историю изменений CustomerType с такимже id.

Типичный результат таков:
CustomerType
Код

id  type      active  effectiveDate        expirationDate  operator   
--  --------  ------  -------------------  --------------  ---------  
1   Private   false   2009-01-08 15:36:53  (null)          ANONYMOUS  
2   Business  true    2009-01-08 15:34:23  (null)          ANONYMOUS  
3   Business  true    2009-01-08 15:34:24  (null)          ANONYMOUS  
4   Private   false   2009-01-08 15:36:51  (null)          ANONYMOUS  
5   Business  true    2009-01-08 15:36:53  (null)          ANONYMOUS  


CustomerTypeHist
Код

recId  id  type      active  effectiveDate        expirationDate       operator   
-----  --  --------  ------  -------------------  -------------------  ---------  
1      1   Business  true    2009-01-08 15:33:09  2009-01-08 15:34:22  ANONYMOUS  
2      1   Business  false   2009-01-08 17:03:51  2009-01-08 15:34:23  ANONYMOUS  
3      3   Business  true    2009-01-08 15:33:17  2009-01-08 15:34:24  ANONYMOUS  
4      4   Business  true    2009-01-08 15:36:51  2009-01-08 15:36:51  ANONYMOUS  
5      1   Business  true    2009-01-08 15:34:23  2009-01-08 15:36:53  ANONYMOUS  


Ну вот вроде и всё.

Теслировал через Bean.

Добавлено через 4 минуты и 52 секунды
А если не хочется писать по 3 класса на каждую сущность, то MappedSuperclass можно превратить во что то более абстрактное, что подходит для всех сущностей которым нужно сохранение истории, и пользоваться им где надо.

P.S. Любые поправки и предложения с радостью принимаются.
  smile 

Это сообщение отредактировал(а) ShurikA - 9.1.2009, 04:07


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 9.1.2009, 09:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Осталось ещё кое что сделать:
надо бы это дело автоматизировать. Я просто не нащёл путь сделать Dependency Injection в Entity, но идя в том что бы в @PreUpdate сущность сама записывала историю.


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 9.1.2009, 12:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  6.1.2009,  13:15 Найти цитируемый пост)
2) Сделать так что бы слушатель сущностей сам находил подходящего EntityManager-а. Например через JNDI lookup.

Как это сделать?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 9.1.2009, 12:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



вот таким образом это не работает:
Код

@Entity
@Table(name = "CustomerType")
@NamedQueries({
    @NamedQuery(name = "CustomerType.findAll", query = "SELECT c FROM CustomerType c")
})
@PersistenceContext(unitName = "CRM-ejbPU", name = "persistence/em")
public class CustomerType extends CustomerTypeSuper {

    private static final long serialVersionUID = 1L;

    public CustomerType() {
    }

    @PreUpdate
    protected void preUpdate() {
        EntityManager em;
        System.out.println("PreUpdate");
        try {
            em = (EntityManager) (new InitialContext()).lookup("java:comp/env/persistence/em");

            Date editDate = new Date();
            //record to history
            CustomerTypeHist typeHist = new CustomerTypeHist();
            typeHist.cloneSource(this);
            typeHist.setExpirationDate(editDate);
            em.persist(typeHist);
            this.setEffectiveDate(editDate);
        } catch (NamingException ex) {
            ex.printStackTrace();
            Logger.getLogger(CustomerTypeSuper.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
...
}


Выдаёт:
Код

Exception [TOPLINK-28018] (Oracle TopLink Essentials - 2.0.1 (Build b04-fcs (04/11/2008))): oracle.toplink.essentials.exceptions.EntityManagerSetupException
Exception Description: predeploy for PersistenceUnit [CRM-ejbPU] failed.
Internal Exception: Exception [TOPLINK-7212] (Oracle TopLink Essentials - 2.0.1 (Build b04-fcs (04/11/2008))): oracle.toplink.essentials.exceptions.ValidationException
Exception Description: The attribute [effectiveDate] from the entity class [class crm.config.custType.entities.CustomerType] does not specify a temporal type. A temporal type must be specified for persistent fields or properties of type java.util.Date and java.util.Calendar.
javax.persistence.PersistenceException: Exception [TOPLINK-28018] (Oracle TopLink Essentials - 2.0.1 (Build b04-fcs (04/11/2008))): oracle.toplink.essentials.exceptions.EntityManagerSetupException
Exception Description: predeploy for PersistenceUnit [CRM-ejbPU] failed.
Internal Exception: Exception [TOPLINK-7212] (Oracle TopLink Essentials - 2.0.1 (Build b04-fcs (04/11/2008))): oracle.toplink.essentials.exceptions.ValidationException
Exception Description: The attribute [effectiveDate] from the entity class [class crm.config.custType.entities.CustomerType] does not specify a temporal type. A temporal type must be specified for persistent fields or properties of type java.util.Date and java.util.Calendar.
        at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:643)
        at oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.createContainerEntityManagerFactory(EntityManagerFactoryProvider.java:244)
        at com.sun.enterprise.server.PersistenceUnitLoaderImpl.load(PersistenceUnitLoaderImpl.java:149)
        at com.sun.enterprise.server.PersistenceUnitLoaderImpl.load(PersistenceUnitLoaderImpl.java:84)
...



--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 9.1.2009, 19:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Самый простой в данной ситуации (когда ну просто никак не избежать доступа к БД из слушателя сущностей) способ это создать EJB компонент с @Remote интерфейсом, в который будет инжектироваться EntityManager. Из слушателя получать доступ к бину.

Код

@Stateless
@Remote(IDaoBean.class)
public class DaoBean implements IDaoBean
{
    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager()
    {
        return entityManager;
    }
}
 

В коде слушателя:
Код

@PreUpdate
protected void preUpdate() {
    InitialContext context = new InitialContext();
    IDaoBean iDaoBean = (IDaoBean) context.lookup(IDaoBean.class.getName());
}


Но! Мне такой подход не очень нравится. Прямо "Паблик Морозов" какой-то получился... никак не "сервис-локатор"... Поэтому я предлагаю общую логику работы с БД вынести в DaoBean и пусть он будет настоящим Dao сервисом.

Добавлено через 3 минуты и 11 секунд
Да, замечу, что доступ к EJB через JNDI весьма зависит от сервера приложений что вы используете. Данный вариант был описан для Glassfish. ЕМНИП, на JBoss необязательно делать бин с удаленным интрефейсом, можно и до локального простым лукапом достучаться.


--------------------
user posted image нет времени думать - нужно писать КОД!

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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



powerOn
Кстати, есть какая то причина что PreUpdate не вызывается если он прописан в MappedSuperclass, а не в самой сущности?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 10.1.2009, 13:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  10.1.2009,  02:17 Найти цитируемый пост)
Кстати, есть какая то причина что PreUpdate не вызывается если он прописан в MappedSuperclass, а не в самой сущности? 


В спеке написано что можно и в mapped superclass слушателей определять. Давай код что-ли посмотрим...


--------------------
user posted image нет времени думать - нужно писать КОД!

PM MAIL   Вверх
ShurikA
Дата 11.1.2009, 07:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  10.1.2009,  12:27 Найти цитируемый пост)
В спеке написано что можно и в mapped superclass слушателей определять. Давай код что-ли посмотрим... 

Я где то выкопал что это баг. Сейчас не могу найти где?

Добавлено через 1 минуту и 57 секунд
Но на самом деле он мне в суперклассе не очень нужен. Можно было бы эту функцию сделать generic абсолютно для всех, но она будет немногим медленная.


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
sandello
Дата 11.1.2009, 12:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

Репутация: нет
Всего: 2



Похожую, если не именно эту, задачу решает проект Envers. Правда, он обладает кучей ограничений. К примеру, заточен под hibernate smile


--------------------
user posted image
PM MAIL Jabber   Вверх
ShurikA
Дата 12.1.2009, 12:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Воткнулся в очередную проблемму:
Код

Exception Description: The attribute [effectiveDate] from the entity class [class crm.config.custType.entities.CustomerType] does not specify a temporal type. A temporal type must be specified for persistent fields or properties of type java.util.Date and java.util.Calendar.


При этом CustomerType выглядет так:
Код

@MappedSuperclass
public class CustomerTypeSuper implements Serializable {

    protected static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "id", nullable = false)
    protected Integer id;
    @Basic(optional = false)
    @Column(name = "type", nullable = false, length = 50)
    protected String type;
    @Basic(optional = false)
    @Column(name = "active", nullable = false)
    protected boolean active;
    @Basic(optional = false)
    @Column(name = "effectiveDate", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    protected Date effectiveDate;
    @Column(name = "expirationDate")
    @Temporal(TemporalType.TIMESTAMP)
    protected Date expirationDate;
    @Basic(optional = false)
    @Column(name = "operator", nullable = false, length = 50)
    protected String operator;

    public CustomerTypeSuper() {
    }

    public CustomerTypeSuper(Integer id) {
        this.id = id;
    }

    public CustomerTypeSuper(Integer id, String type, boolean active, Date effectiveDate, String operator) {
        this.id = id;
        this.type = type;
        this.active = active;
        this.effectiveDate = effectiveDate;
        this.operator = operator;
    }

    public Integer getId() {
        return id;
    }

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

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public Date getEffectiveDate() {
        return effectiveDate;
    }

    public void setEffectiveDate(Date effectiveDate) {
        this.effectiveDate = effectiveDate;
    }

    public Date getExpirationDate() {
        return expirationDate;
    }

    public void setExpirationDate(Date expirationDate) {
        this.expirationDate = expirationDate;
    }

    public String getOperator() {
        return operator;
    }

    public void setOperator(String operator) {
        this.operator = operator;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof CustomerTypeSuper)) {
            return false;
        }
        CustomerTypeSuper other = (CustomerTypeSuper) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "crm.config.custType.entities.CustomerType[id=" + id + "]";
    }
}


Код

@Entity
@Table(name = "CustomerType", catalog = "CRM", schema = "")
@NamedQueries({@NamedQuery(name = "CustomerType.findAll", query = "SELECT c FROM CustomerType c")})
public class CustomerType extends CustomerTypeSuper {

    private static final long serialVersionUID = 1L;

    public CustomerType() {
    }

    @PreUpdate
    protected void preUpdate() {
        Date editDate = new Date();
        CustomerTypeHist histType = new CustomerTypeHist();
        histType.cloneSource(this);
        histType.setExpirationDate(editDate);
        this.setEffectiveDate(editDate);
        try {
            //find the EJC to save the history entity
            InitialContext context = new InitialContext();
            CustomerTypeRemote aMgr = (CustomerTypeRemote) context.lookup(CustomerTypeRemote.class.getName());
            aMgr.persist(histType);
        } catch (NamingException ex) {
            ex.printStackTrace();
            Logger.getLogger(CustomerType.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof CustomerType)) {
            return false;
        }
        CustomerType other = (CustomerType) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "crm.config.custType.entities.CustomerType[id=" + id + "]";
    }
}



--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 12.1.2009, 14:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Если анотацию @PreUpdate убрать, то всё ОК.   smile 


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 13.1.2009, 09:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



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


--------------------
user posted image нет времени думать - нужно писать КОД!

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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



powerOn
То есть мне нужно все мои анотации перенести на методы?
Это достаточно сделать только в одном классе?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 13.1.2009, 10:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  13.1.2009,  10:04 Найти цитируемый пост)
powerOn, 
То есть мне нужно все мои анотации перенести на методы?
Это достаточно сделать только в одном классе? 


Попробуй. Если будет не достаточно, то значит во всех.



--------------------
user posted image нет времени думать - нужно писать КОД!

PM MAIL   Вверх
ShurikA
Дата 14.1.2009, 03:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



если класс(Entity)  порождается от папы а папа в свою очередь от его папы, и папа и дедушка должны быть @MappedSuperclass ?


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
ShurikA
Дата 14.1.2009, 10:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  13.1.2009,  08:55 Найти цитируемый пост)
все аннотации либо всегда указывают на методах, либо всегда на полях. т.е. изначально выбирается один стиль. в твоем случае нужно указывать на методах, поскольку @PreUpdate на поле нельзя указать. 

OK, вроде всё работает и вызывается.

Следуюшая проблемма smile
выглядит так как будто @PreUpdate вызывается после того как поля уже поменянны, как можно отловить момент до того как они меняются?

Это сообщение отредактировал(а) ShurikA - 14.1.2009, 10:34


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 14.1.2009, 12:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  14.1.2009,  10:05 Найти цитируемый пост)
выглядит так как будто @PreUpdate вызывается после того как поля уже поменянны, как можно отловить момент до того как они меняются?


Не понял. PreUpdate вызывается перед обновлением данных в БД.



--------------------
user posted image нет времени думать - нужно писать КОД!

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


Зануда
***


Профиль
Группа: Завсегдатай
Сообщений: 1364
Регистрация: 29.10.2005
Где: Канада

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



Цитата(powerOn @  14.1.2009,  11:18 Найти цитируемый пост)
Не понял. PreUpdate вызывается перед обновлением данных в БД.

Правильно, но поля обьекта уже изменены к тому времени.


--------------------
Если долго мучиться, что нибудь получится...
user posted image
PM MAIL WWW ICQ Skype   Вверх
powerOn
Дата 14.1.2009, 22:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(ShurikA @  14.1.2009,  21:17 Найти цитируемый пост)
Правильно, но поля обьекта уже изменены к тому времени. 


Ну это уже не в компетенции JPA следить за такими вещами, хотя оно и понимает что объект нужно обновить в БД... 
В остальном, думаю, нужно разрабатывать собственное решение.



--------------------
user posted image нет времени думать - нужно писать КОД!

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


Шустрый
*


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

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



Уже говорили, но если вы используете hibernate-entitymanager - таки посмотрите на проект envers. 

Он делает как раз то что вам надо - вешается на PostInsert, PostUpdate, PostDelete события и протоколирует изменение выбранных entities. 

Единственный минус - к аннотациям @Entity и @Table прибавляется еще одна - @Audited, т.е. envers всегда должен будет быть хотя-бы в classpath.

С другой стороны - скоро envers будет модулем hibernate и так или иначе будет в classpath.


PM MAIL   Вверх
powerOn
Дата 15.1.2009, 19:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


software saboteur
****


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

Репутация: 28
Всего: 159



Цитата(chand0s @  15.1.2009,  14:36 Найти цитируемый пост)
Уже говорили, но если вы используете hibernate-entitymanager - таки посмотрите на проект envers. 

Он делает как раз то что вам надо - вешается на PostInsert, PostUpdate, PostDelete события и протоколирует изменение выбранных entities. 

Единственный минус - к аннотациям @Entity и @Table прибавляется еще одна - @Audited, т.е. envers всегда должен будет быть хотя-бы в classpath.

С другой стороны - скоро envers будет модулем hibernate и так или иначе будет в classpath.


используется Toplink.



--------------------
user posted image нет времени думать - нужно писать КОД!

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

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

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


 




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


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

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