Contents

Hibernate中@Immutable注解

1. 概述

在本文中,我们将讨论如何在 Hibernate中使实体、集合或属性不可变

默认情况下,字段是可变的,这意味着我们能够对它们执行更改其状态的操作。

2. Maven

为了让我们的项目启动并运行,我们首先需要将必要的依赖项添加到我们的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注解添加到我们的实体中,所以如果我们尝试保存一个Event*:

@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注解,但这只会在我们创建实体时产生影响。这是因为它指定了 id 的生成策略——由于Immutable注解,任何其他操作都不会影响Id字段。

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 将简单地忽略update操作而不抛出异常。但是,如果我们删除*@Immutable*注解,我们会得到不同的结果:

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

这告诉我们的是我们的对象现在是可变的(如果我们不包含注解,mutable是默认值)并且将允许更新完成它的工作。

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.blogdemo.entities.Event.guestList#1]

这次我们遇到了一个异常,因为对于集合,我们不允许添加或删除它们。

4.1. 删除集合

另一种情况是Collection是不可变的,它会在我们尝试删除并且我们设置了*@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.blogdemo.entities.Event.guestList#1]

5. XML 注解

最后,也可以通过mutable=false属性使用 XML 进行配置:

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

但是,由于我们基本上使用注解方法实现了示例,因此我们不会使用 XML 进行详细介绍。