Immutability

Immutability can be specified for both entities and collections.

Entity immutability

If a specific entity is immutable, it is good practice to mark it with the @Immutable annotation.

Example 1. Immutable entity
@Entity(name = "Event")
@Immutable
public static class Event {

    @Id
    private Long id;

    private Date createdOn;

    private String message;

    //Getters and setters are omitted for brevity

}

Internally, Hibernate is going to perform several optimizations, such as:

  • reducing memory footprint since there is no need to retain the dehydrated state for the dirty checking mechanism

  • speeding-up the Persistence Context flushing phase since immutable entities can skip the dirty checking process

Considering the following entity is persisted in the database:

Example 2. Persisting an immutable entity
doInJPA( this::entityManagerFactory, entityManager -> {
    Event event = new Event();
    event.setId( 1L );
    event.setCreatedOn( new Date( ) );
    event.setMessage( "Hibernate User Guide rocks!" );

    entityManager.persist( event );
} );

When loading the entity and trying to change its state, Hibernate will skip any modification, therefore no SQL UPDATE statement is executed.

Example 3. The immutable entity ignores any update
doInJPA( this::entityManagerFactory, entityManager -> {
    Event event = entityManager.find( Event.class, 1L );
    log.info( "Change event message" );
    event.setMessage( "Hibernate User Guide" );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
    Event event = entityManager.find( Event.class, 1L );
    assertEquals("Hibernate User Guide rocks!", event.getMessage());
} );
SELECT e.id AS id1_0_0_,
       e.createdOn AS createdO2_0_0_,
       e.message AS message3_0_0_
FROM   event e
WHERE  e.id = 1

-- Change event message

SELECT e.id AS id1_0_0_,
       e.createdOn AS createdO2_0_0_,
       e.message AS message3_0_0_
FROM   event e
WHERE  e.id = 1

Collection immutability

Just like entities, collections can also be marked with the @Immutable annotation.

Considering the following entity mappings:

Example 4. Immutable collection
@Entity(name = "Batch")
public static class Batch {

    @Id
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL)
    @Immutable
    private List<Event> events = new ArrayList<>( );

    //Getters and setters are omitted for brevity

}

@Entity(name = "Event")
@Immutable
public static class Event {

    @Id
    private Long id;

    private Date createdOn;

    private String message;

    //Getters and setters are omitted for brevity

}

This time, not only the Event entity is immutable, but the Event collection stored by the Batch parent entity. Once the immutable collection is created, it can never be modified.

Example 5. Persisting an immutable collection
doInJPA( this::entityManagerFactory, entityManager -> {
    Batch batch = new Batch();
    batch.setId( 1L );
    batch.setName( "Change request" );

    Event event1 = new Event();
    event1.setId( 1L );
    event1.setCreatedOn( new Date( ) );
    event1.setMessage( "Update Hibernate User Guide" );

    Event event2 = new Event();
    event2.setId( 2L );
    event2.setCreatedOn( new Date( ) );
    event2.setMessage( "Update Hibernate Getting Started Guide" );

    batch.getEvents().add( event1 );
    batch.getEvents().add( event2 );

    entityManager.persist( batch );
} );

The Batch entity is mutable. Only the events collection is immutable.

For instance, we can still modify the entity name:

Example 6. Changing the mutable entity
doInJPA( this::entityManagerFactory, entityManager -> {
    Batch batch = entityManager.find( Batch.class, 1L );
    log.info( "Change batch name" );
    batch.setName( "Proposed change request" );
} );
SELECT b.id AS id1_0_0_,
       b.name AS name2_0_0_
FROM   Batch b
WHERE  b.id = 1

-- Change batch name

UPDATE batch
SET    name = 'Proposed change request'
WHERE  id = 1

However, when trying to modify the events collection:

Example 7. Immutable collections cannot be modified
try {
    doInJPA( this::entityManagerFactory, entityManager -> {
        Batch batch = entityManager.find( Batch.class, 1L );
        batch.getEvents().clear();
    } );
}
catch ( Exception e ) {
    log.error( "Immutable collections cannot be modified" );
}
javax.persistence.RollbackException: Error while committing the transaction

Caused by: javax.persistence.PersistenceException: org.hibernate.HibernateException:

Caused by: org.hibernate.HibernateException: changed an immutable collection instance: [
    org.hibernate.userguide.immutability.CollectionImmutabilityTest$Batch.events#1
]

While immutable entity changes are simply discarded, modifying an immutable collection end up in a HibernateException being thrown.