Перейти к основному содержимому

@Immutable в спящем режиме

· 4 мин. чтения

1. Обзор

В этой статье мы поговорим о том, как сделать сущность, коллекцию или атрибут неизменяемыми в Hibernate.

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

2. Мавен

Чтобы запустить наш проект, нам сначала нужно добавить необходимые зависимости в наш pom.xml . И поскольку мы работаем с Hibernate, мы собираемся добавить соответствующую зависимость :

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.7.Final</version>
</dependency>

И, поскольку мы работаем с HSQLDB , нам также необходимо:

<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.4</version>
</dependency>

3. Аннотация к сущностям

Во-первых, давайте определим простой класс сущности:

@Entity
@Immutable
@Table(name = "events_generated")
public class EventGeneratedId {

@Id
@Column(name = "event_generated_id")
@GeneratedValue(generator = "increment")
@GenericGenerator(name = "increment", strategy = "increment")
private Long id;

@Column(name = "name")
private String name;
@Column(name = "description")
private String description;

// standard setters and getters
}

Как вы заметили, мы уже добавили аннотацию @Immutable к нашей сущности, поэтому, если мы попытаемся сохранить событие :

@Test
public void addEvent() {
Event event = new Event();
event.setId(2L);
event.setTitle("Public Event");
session.save(event);
session.getTransaction().commit();
session.close();
}

Затем мы должны получить вывод:

Hibernate: insert into events (title, event_id) values (?, ?)

Вывод должен быть таким же, даже если мы удалим аннотацию, что означает отсутствие эффекта при попытке добавить объект независимо от аннотации.

Также важно отметить, что в нашу сущность EventGeneratedId мы добавили аннотацию GeneratedValue , но это будет иметь значение только при создании сущности. Это потому, что он определяет стратегию генерации идентификатора — любые другие операции не повлияют на поле идентификатора из-за аннотации Immutable .

3.1. Обновление объекта

Теперь у нас не было проблем с сохранением объекта, давайте попробуем его обновить:

@Test
public void updateEvent() {
Event event = (Event) session.createQuery(
"FROM Event WHERE title='My Event'").list().get(0);
event.setTitle("Public Event");
session.saveOrUpdate(event);
session.getTransaction().commit();
}

Hibernate просто проигнорирует операцию обновления без создания исключения. Однако, если мы удалим аннотацию @Immutable , мы получим другой результат:

Hibernate: select ... from events where title='My Event'
Hibernate: update events set title=? where event_id=?

Это говорит нам о том, что наш объект теперь изменчив ( изменяемый — это значение по умолчанию, если мы не включаем аннотацию) и позволит обновлению выполнять свою работу.

3.2. Удаление объекта

Когда дело доходит до удаления сущности:

@Test
public void deleteEvent() {
Event event = (Event) session.createQuery(
"FROM Event WHERE title='My Event'").list().get(0);
session.delete(event);
session.getTransaction().commit();
}

Мы сможем выполнить удаление независимо от того, является ли оно изменчивым или нет:

Hibernate: select ... from events where title='My Event'
Hibernate: delete from events where event_id=?

4. Аннотация к коллекциям

До сих пор мы видели, что аннотация делает с сущностями, но, как мы упоминали в начале, ее также можно применять к коллекциям.

Во-первых, давайте добавим коллекцию в наш класс Event :

@Immutable
public Set<String> getGuestList() {
return guestList;
}

Как и раньше, мы добавили аннотацию заранее, поэтому, если мы продолжим и попытаемся добавить элемент в нашу коллекцию:

org.hibernate.HibernateException: 
changed an immutable collection instance: [com.foreach.entities.Event.guestList#1]

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

4.1. Удаление коллекций

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

Итак, всякий раз, когда присутствует @Immutable , и мы пытаемся удалить:

@Test
public void deleteCascade() {
Event event = (Event) session.createQuery(
"FROM Event WHERE title='Public Event'").list().get(0);
String guest = event.getGuestList().iterator().next();
event.getGuestList().remove(guest);
session.saveOrUpdate(event);
session.getTransaction().commit();
}

Выход:

org.hibernate.HibernateException: 
changed an immutable collection instance:
[com.foreach.entities.Event.guestList#1]

5. XML-примечания

Наконец, конфигурацию также можно выполнить с помощью XML через атрибут mutable=false :

<hibernate-mapping>
<class name="com.foreach.entities.Event" mutable="false">
<id name="id" column="event_id">
<generator class="increment"/>
</id>
<property name="title"/>
</class>
</hibernate-mapping>

Однако, поскольку мы в основном реализовывали примеры с помощью метода аннотации, мы не будем вдаваться в подробности, используя XML.

6. Заключение

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

Как всегда, реализацию всех этих примеров и сниппетов можно найти в проекте GitHub . Это проект на основе Maven, поэтому его легко импортировать и запускать.