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, поэтому его легко импортировать и запускать.