Животен цикъл на обекта в хибернация

1. Общ преглед

Всеки обект в хибернация естествено има жизнен цикъл в рамките - той е или в преходно, управлявано, отделено или изтрито състояние.

Разбирането на тези състояния както на концептуално, така и на техническо ниво е от съществено значение за правилното използване на хибернацията.

За да научите за различни методи за хибернация, които се занимават с обекти, разгледайте един от предишните ни уроци.

2. Помощни методи

По време на този урок последователно ще използваме няколко помощни метода:

  • HibernateLifecycleUtil.getManagedEntities (сесия) - ще го използваме, за да получим всички управлявани обекти от вътрешното хранилище на сесия
  • DirtyDataInspector.getDirtyEntities () - ще използваме този метод, за да получим списък на всички обекти, маркирани като „мръсни“
  • HibernateLifecycleUtil.queryCount (заявка) - удобен метод за извършване на заявка за броене (*) спрямо вградената база данни

Всички горепосочени помощни методи се импортират статично за по-добра четливост. Можете да намерите техните имплементации в проекта GitHub, свързан в края на тази статия.

3. Всичко е свързано с контекста на постоянство

Преди да влезем в темата за жизнения цикъл на субекта, първо, трябва да разберем контекста на постоянство .

Просто казано, контекстът на постоянство се намира между клиентския код и хранилището на данни . Това е етапна област, където постоянните данни се преобразуват в обекти, готови за четене и промяна от клиентски код.

Теоретично казано, контекстът на постоянство е изпълнение на модела на единица работа. Той проследява всички заредени данни, проследява промените на тези данни и е отговорен в крайна сметка да синхронизира всички промени обратно към базата данни в края на бизнес транзакцията.

JPA EntityManager и Hibernate's Session са изпълнение на концепцията за устойчивост . В тази статия ще използваме Hibernate Session, за да представим контекста на постоянство.

Състоянието на жизнения цикъл на обекта в хибернация обяснява как обектът е свързан с контекста на постоянство , както ще видим по-нататък.

4. Управляван субект

Управляван обект е представяне на ред на таблица на базата данни (въпреки че този ред все още не трябва да съществува в базата данни).

Това се управлява от текущо изпълняваната сесия и всяка промяна, направена в нея, ще бъде проследена и разпространена автоматично в базата данни .

На сесията или товари на предприятието от базата данни или повторни аташета самостоятелна единица. Ще обсъдим отделените обекти в раздел 5.

Нека да наблюдаваме някакъв код, за да получим пояснение.

Нашето примерно приложение дефинира един обект, клас FootballPlayer . При стартиране ще инициализираме хранилището с някои примерни данни:

+-------------------+-------+ | Name | ID | +-------------------+-------+ | Cristiano Ronaldo | 1 | | Lionel Messi | 2 | | Gianluigi Buffon | 3 | +-------------------+-------+

Да кажем, че искаме да променим името на Буфон, за да започнем с него - искаме да поставим пълното му име Джанлуиджи Буфон вместо Джиджи Буфон.

Първо, трябва да започнем нашата работна единица, като получим сесия:

Session session = sessionFactory.openSession();

В сървърна среда можем да инжектираме сесия в нашия код чрез контекстно ориентиран прокси сървър. Принципът остава същият: имаме нужда от сесия, която да капсулира бизнес транзакцията на нашата работна единица.

След това ще инструктираме нашата сесия да зареди данните от постоянното хранилище:

assertThat(getManagedEntities(session)).isEmpty(); List players = s.createQuery("from FootballPlayer").getResultList(); assertThat(getManagedEntities(session)).size().isEqualTo(3); 

Когато за първи път получаваме сесия , нейното постоянно хранилище на контекст е празно, както е показано от първия ни израз на утвърждаване .

След това изпълняваме заявка, която извлича данни от базата данни, създава представяне на обект на данните и накрая връща обекта, който да използваме.

Вътрешно сесията проследява всички обекти, които зарежда в постоянното контекстно хранилище. В нашия случай вътрешното хранилище на сесията ще съдържа 3 обекта след заявката.

Сега нека променим името на Джиджи:

Transaction transaction = session.getTransaction(); transaction.begin(); FootballPlayer gigiBuffon = players.stream() .filter(p -> p.getId() == 3) .findFirst() .get(); gigiBuffon.setName("Gianluigi Buffon"); transaction.commit(); assertThat(getDirtyEntities()).size().isEqualTo(1); assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Gianluigi Buffon");

4.1. Как работи?

При повикване към запис на транзакция () или измиване () , сесията ще намери всички замърсени обекти от своя списък за проследяване и ще синхронизира състоянието с базата данни.

Забележете, че не трябваше да извикваме никакъв метод, за да уведомим Session, че сме променили нещо в нашия обект - тъй като това е управляван обект, всички промени се разпространяват автоматично в базата данни.

Управляваният обект винаги е постоянен обект - той трябва да има идентификатор на базата данни, въпреки че представянето на редовете на базата данни все още не е създадено, т.е. операторът INSERT чака края на единицата работа.

Вижте главата за преходните обекти по-долу.

5. Отделен субект

A detached entity is just an ordinary entity POJO whose identity value corresponds to a database row. The difference from a managed entity is that it's not tracked anymore by any persistence context.

An entity can become detached when the Session used to load it was closed, or when we call Session.evict(entity) or Session.clear().

Let's see it in the code:

FootballPlayer cr7 = session.get(FootballPlayer.class, 1L); assertThat(getManagedEntities(session)).size().isEqualTo(1); assertThat(getManagedEntities(session).get(0).getId()).isEqualTo(cr7.getId()); session.evict(cr7); assertThat(getManagedEntities(session)).size().isEqualTo(0);

Our persistence context will not track the changes in detached entities:

cr7.setName("CR7"); transaction.commit(); assertThat(getDirtyEntities()).isEmpty();

Session.merge(entity)/Session.update(entity) can (re)attach a session:

FootballPlayer messi = session.get(FootballPlayer.class, 2L); session.evict(messi); messi.setName("Leo Messi"); transaction.commit(); assertThat(getDirtyEntities()).isEmpty(); transaction = startTransaction(session); session.update(messi); transaction.commit(); assertThat(getDirtyEntities()).size().isEqualTo(1); assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Leo Messi");

For reference on both Session.merge() and Session.update() see here.

5.1. The Identity Field Is All That Matters

Let's have a look at the following logic:

FootballPlayer gigi = new FootballPlayer(); gigi.setId(3); gigi.setName("Gigi the Legend"); session.update(gigi);

In the example above, we've instantiated an entity in the usual way via its constructor. We've populated the fields with values and we've set the identity to 3, which corresponds to the identity of persistent data that belongs to Gigi Buffon. Calling update() has exactly the same effect as if we've loaded the entity from another persistence context.

In fact, Session doesn't distinguish where a re-attached entity originated from.

It's quite a common scenario in web applications to construct detached entities from HTML form values.

As far as Session is concerned, a detached entity is just a plain entity whose identity value corresponds to persistent data.

Be aware that the example above just serves a demo purpose. and we need to know exactly what we're doing. Otherwise, we could end up with null values across our entity if we just set the value on the field we want to update, leaving the rest untouched (so, effectively null).

6. Transient Entity

A transient entity is simply an entity object that has no representation in the persistent store and is not managed by any Session.

A typical example of a transient entity would be instantiating a new entity via its constructor.

To make a transient entity persistent, we need to call Session.save(entity) or Session.saveOrUpdate(entity):

FootballPlayer neymar = new FootballPlayer(); neymar.setName("Neymar"); session.save(neymar); assertThat(getManagedEntities(session)).size().isEqualTo(1); assertThat(neymar.getId()).isNotNull(); int count = queryCount("select count(*) from Football_Player where name="Neymar""); assertThat(count).isEqualTo(0); transaction.commit(); count = queryCount("select count(*) from Football_Player where name="Neymar""); assertThat(count).isEqualTo(1);

As soon as we execute Session.save(entity), the entity is assigned an identity value and becomes managed by the Session. However, it might not yet be available in the database as the INSERT operation might be delayed until the end of the unit of work.

7. Deleted Entity

An entity is in a deleted (removed) state if Session.delete(entity) has been called, and the Session has marked the entity for deletion. The DELETE command itself might be issued at the end of the unit of work.

Let's see it in the following code:

session.delete(neymar); assertThat(getManagedEntities(session).get(0).getStatus()).isEqualTo(Status.DELETED);

However, notice that the entity stays in the persistent context store until the end of the unit of work.

8. Conclusion

Концепцията за контекст на постоянство е от основно значение за разбирането на жизнения цикъл на хибернати. Изяснихме жизнения цикъл, като разгледахме примерите за кодове, демонстриращи всеки статус.

Както обикновено, кодът, използван в тази статия, може да бъде намерен в GitHub.