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.
@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:
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.
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:
@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.
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:
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:
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 |