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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> История Entity 
V
    Опции темы
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   Вверх
Страницы: (4) Все 1 [2] 3 4 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "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.0685 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


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

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