1. Overview

In this article, we’ll talk about how we can make an entity, collection or attribute Immutable in Hibernate.

By default, fields are mutable, which means we’re able to perform operations on them that change their state.

2. Maven

To get our project up and running, we first need to add the necessary dependencies to our pom.xml. And as we’re working with Hibernate, we are going to add the corresponding dependency:

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

And, because we are working with HSQLDB, we also need:

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

3. Annotation on Entities

First, let’s define a simple entity class:

@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
}

As you have noticed we have added already the @Immutable annotation to our entity, so if we try and save an Event:

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

Then we should get the output:

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

The output should be the same even if we remove the annotation, meaning there’s no effect when we try to add an entity regardless of the annotation.

It’s also important to note that in our EventGeneratedId entity, we added the GeneratedValue annotation, but this will only make a difference when we’re creating an entity. That’s because it specifies the generation strategy for the id – any other operations will not affect the Id field due to the Immutable annotation.

3.1. Updating the Entity

Now, we had no issue saving an entity, let’s try to update it:

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

Hibernate will simply ignore the update operation without throwing an exception. However, if we remove the @Immutable annotation we get a different result:

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

What this tells us is that our object is now mutable (mutable is the default value if we don’t include the annotation) and will allow the update to do its job.

3.2. Deleting an Entity

When it comes to deleting an entity:

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

We’ll be able to perform the delete regardless if it is mutable or not:

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

4. Annotation on Collections

So far we’ve seen what the annotation does to entities, but as we mentioned in the beginning, it can also be applied to collections.

First, let’s add a collection to our Event class:

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

Same as before, we’ve added the annotation beforehand, so if we go ahead and try to add an element to our collection:

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

This time we get an exception because with collections we are not allowed to add nor delete them.

4.1. Deleting Collections

The other scenario where a Collection by being immutable will throw an exception it’s whenever we try to delete and we have set the @Cascade annotation.

So, whenever @Immutable is present and we attempt to delete:

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

Output:

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

5. XML Notes

Finally, the configuration can also be done using XML through the mutable=false attribute:

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

However, since we basically implemented the examples using the annotation method, we will not get into details using XML.

6. Conclusion

In this quick article, we explore the useful @Immutable annotation out of Hibernate, and how that can help us define better semantics and constraints on our data.

As always, the implementation of all of these examples and snippets can be found in the GitHub project. This is a Maven-based project so it should be easy to import and run.

Course – LSD (cat=Persistence)

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE
res – Persistence (eBook) (cat=Persistence)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.