Hibernate.org Community Documentation

- Prev
- Next

## Chapter 12. Read-only entities

## Important

Hibernate's treatment of read-only entities may differ from what you may have encountered elsewhere. Incorrect usage may cause unexpected results.

When an entity is read-only:

- Hibernate does not dirty-check the entity's simple properties or single-ended associations;

- Hibernate will not update simple properties or updatable single-ended associations;

- Hibernate will not update the version of the read-only entity if only simple properties or single-ended updatable associations are changed;

In some ways, Hibernate treats read-only entities the same as entities that are not read-only:

- Hibernate cascades operations to associations as defined in the entity mapping.

- Hibernate updates the version if the entity has a collection with changes that dirties the entity;

- A read-only entity can be deleted.

Even if an entity is not read-only, its collection association can be affected if it contains a read-only entity.

For details about the affect of read-only entities on different property and association types, see Section 12.2, “Read-only affect on property type”.

For details about how to make entities read-only, see Section 12.1, “Making persistent entities read-only”

Hibernate does some optimizing for read-only entities:

- It saves execution time by not dirty-checking simple properties or single-ended associations.

- It saves memory by deleting database snapshots.

## 12.1. Making persistent entities read-only

Only persistent entities can be made read-only. Transient and detached entities must be put in persistent state before they can be made read-only.

Hibernate provides the following ways to make persistent entities read-only:

- you can map an entity class as immutable; when an entity of an immutable class is made persistent, Hibernate automatically makes it read-only. see Section 12.1.1, “Entities of immutable classes” for details

- you can change a default so that entities loaded into the session by Hibernate are automatically made read-only; see Section 12.1.2, “Loading persistent entities as read-only” for details

- you can make an HQL query or criteria read-only so that entities loaded when the query or criteria executes, scrolls, or iterates, are automatically made read-only; see Section 12.1.3, “Loading read-only entities from an HQL query/criteria” for details

- you can make a persistent entity that is already in the in the session read-only; see Section 12.1.4, “Making a persistent entity read-only” for details

### 12.1.1. Entities of immutable classes

When an entity instance of an immutable class is made persistent, Hibernate automatically makes it read-only.

An entity of an immutable class can created and deleted the same as an entity of a mutable class.

Hibernate treats a persistent entity of an immutable class the same way as a read-only persistent entity of a mutable class. The only exception is that Hibernate will not allow an entity of an immutable class to be changed so it is not read-only.

### 12.1.2. Loading persistent entities as read-only

## Note

Entities of immutable classes are automatically loaded as read-only.

To change the default behavior so Hibernate loads entity instances of mutable classes into the session and automatically makes them read-only, call:

    Session.setDefaultReadOnly( true );

To change the default back so entities loaded by Hibernate are not made read-only, call:

    Session.setDefaultReadOnly( false );

You can determine the current setting by calling:

    Session.isDefaultReadOnly();

If Session.isDefaultReadOnly() returns true, entities loaded by the following are automatically made read-only:

- Session.load()

- Session.get()

- Session.merge()

- executing, scrolling, or iterating HQL queries and criteria; to override this setting for a particular HQL query or criteria see Section 12.1.3, “Loading read-only entities from an HQL query/criteria”

Changing this default has no effect on:

- persistent entities already in the session when the default was changed

- persistent entities that are refreshed via Session.refresh(); a refreshed persistent entity will only be read-only if it was read-only before refreshing

- persistent entities added by the application via Session.persist(), Session.save(), and Session.update() Session.saveOrUpdate()

### 12.1.3. Loading read-only entities from an HQL query/criteria

## Note

Entities of immutable classes are automatically loaded as read-only.

If Session.isDefaultReadOnly() returns false (the default) when an HQL query or criteria executes, then entities and proxies of mutable classes loaded by the query will not be read-only.

You can override this behavior so that entities and proxies loaded by an HQL query or criteria are automatically made read-only.

For an HQL query, call:

    Query.setReadOnly( true );

`Query.setReadOnly( true )` must be called before `Query.list()`, `Query.uniqueResult()`, `Query.scroll()`, or `Query.iterate()`

For an HQL criteria, call:

    Criteria.setReadOnly( true );

`Criteria.setReadOnly( true )` must be called before `Criteria.list()`, `Criteria.uniqueResult()`, or `Criteria.scroll()`

Entities and proxies that exist in the session before being returned by an HQL query or criteria are not affected.

Uninitialized persistent collections returned by the query are not affected. Later, when the collection is initialized, entities loaded into the session will be read-only if Session.isDefaultReadOnly() returns true.

Using `Query.setReadOnly( true )` or `Criteria.setReadOnly( true )` works well when a single HQL query or criteria loads all the entities and intializes all the proxies and collections that the application needs to be read-only.

When it is not possible to load and initialize all necessary entities in a single query or criteria, you can temporarily change the session default to load entities as read-only before the query is executed. Then you can explicitly initialize proxies and collections before restoring the session default.

    Session session = factory.openSession();
    Transaction tx = session.beginTransaction();

    setDefaultReadOnly( true );
    Contract contract =
       ( Contract ) session.createQuery(
               "from Contract where customerName = 'Sherman'" )
               .uniqueResult();
    Hibernate.initialize( contract.getPlan() );
    Hibernate.initialize( contract.getVariations() );
    Hibernate.initialize( contract.getNotes() );
    setDefaultReadOnly( false );
    ...
    tx.commit();
    session.close();

If Session.isDefaultReadOnly() returns true, then you can use Query.setReadOnly( false ) and Criteria.setReadOnly( false ) to override this session setting and load entities that are not read-only.

### 12.1.4. Making a persistent entity read-only

## Note

Persistent entities of immutable classes are automatically made read-only.

To make a persistent entity or proxy read-only, call:

    Session.setReadOnly(entityOrProxy, true)

To change a read-only entity or proxy of a mutable class so it is no longer read-only, call:

    Session.setReadOnly(entityOrProxy, false)

## Important

When a read-only entity or proxy is changed so it is no longer read-only, Hibernate assumes that the current state of the read-only entity is consistent with its database representation. If this is not true, then any non-flushed changes made before or while the entity was read-only, will be ignored.

To throw away non-flushed changes and make the persistent entity consistent with its database representation, call:

    session.refresh( entity );

To flush changes made before or while the entity was read-only and make the database representation consistent with the current state of the persistent entity:

    // evict the read-only entity so it is detached
    session.evict( entity );

    // make the detached entity (with the non-flushed changes) persistent
    session.update( entity );

    // now entity is no longer read-only and its changes can be flushed
    s.flush();

## 12.2. Read-only affect on property type

The following table summarizes how different property types are affected by making an entity read-only.

Table 12.1. Affect of read-only entity on property types

Property/Association Type | Changes flushed to DB?
--- | ---
Simple (Section 12.2.1, “Simple properties”) | no*
Unidirectional one-to-one Unidirectional many-to-one (Section 12.2.2.1, “Unidirectional one-to-one and many-to-one”) | no* no*
Unidirectional one-to-many Unidirectional many-to-many (Section 12.2.2.2, “Unidirectional one-to-many and many-to-many”) | yes yes
Bidirectional one-to-one (Section 12.2.3.1, “Bidirectional one-to-one”) | only if the owning entity is not read-only*
Bidirectional one-to-many/many-to-one inverse collection non-inverse collection (Section 12.2.3.2, “Bidirectional one-to-many/many-to-one”) | only added/removed entities that are not read-only* yes
Bidirectional many-to-many (Section 12.2.3.3, “Bidirectional many-to-many”) | yes

* Behavior is different when the entity having the property/association is read-only, compared to when it is not read-only.

### 12.2.1. Simple properties

When a persistent object is read-only, Hibernate does not dirty-check simple properties.

Hibernate will not synchronize simple property state changes to the database. If you have automatic versioning, Hibernate will not increment the version if any simple properties change.

    Session session = factory.openSession();
    Transaction tx = session.beginTransaction();

    // get a contract and make it read-only
    Contract contract = ( Contract ) session.get( Contract.class, contractId );
    session.setReadOnly( contract, true );

    // contract.getCustomerName() is "Sherman"
    contract.setCustomerName( "Yogi" );
    tx.commit();

    tx = session.beginTransaction();

    contract = ( Contract ) session.get( Contract.class, contractId );
    // contract.getCustomerName() is still "Sherman"
    ...
    tx.commit();
    session.close();

### 12.2.2. Unidirectional associations

#### 12.2.2.1. Unidirectional one-to-one and many-to-one

Hibernate treats unidirectional one-to-one and many-to-one associations in the same way when the owning entity is read-only.

We use the term unidirectional single-ended association when referring to functionality that is common to unidirectional one-to-one and many-to-one associations.

Hibernate does not dirty-check unidirectional single-ended associations when the owning entity is read-only.

If you change a read-only entity's reference to a unidirectional single-ended association to null, or to refer to a different entity, that change will not be flushed to the database.

## Note

If an entity is of an immutable class, then its references to unidirectional single-ended associations must be assigned when that entity is first created. Because the entity is automatically made read-only, these references can not be updated.

If automatic versioning is used, Hibernate will not increment the version due to local changes to unidirectional single-ended associations.

In the following examples, Contract has a unidirectional many-to-one association with Plan. Contract cascades save and update operations to the association.

The following shows that changing a read-only entity's many-to-one association reference to null has no effect on the entity's database representation.

    // get a contract with an existing plan;
    // make the contract read-only and set its plan to null
    tx = session.beginTransaction();
    Contract contract = ( Contract ) session.get( Contract.class, contractId );
    session.setReadOnly( contract, true );
    contract.setPlan( null );
    tx.commit();

    // get the same contract
    tx = session.beginTransaction();
    contract = ( Contract ) session.get( Contract.class, contractId );

    // contract.getPlan() still refers to the original plan;

    tx.commit();
    session.close();

The following shows that, even though an update to a read-only entity's many-to-one association has no affect on the entity's database representation, flush still cascades the save-update operation to the locally changed association.

    // get a contract with an existing plan;
    // make the contract read-only and change to a new plan
    tx = session.beginTransaction();
    Contract contract = ( Contract ) session.get( Contract.class, contractId );
    session.setReadOnly( contract, true );
    Plan newPlan = new Plan( "new plan"
    contract.setPlan( newPlan);
    tx.commit();

    // get the same contract
    tx = session.beginTransaction();
    contract = ( Contract ) session.get( Contract.class, contractId );
    newPlan = ( Contract ) session.get( Plan.class, newPlan.getId() );

    // contract.getPlan() still refers to the original plan;
    // newPlan is non-null because it was persisted when
    // the previous transaction was committed;

    tx.commit();
    session.close();

#### 12.2.2.2. Unidirectional one-to-many and many-to-many

Hibernate treats unidirectional one-to-many and many-to-many associations owned by a read-only entity the same as when owned by an entity that is not read-only.

Hibernate dirty-checks unidirectional one-to-many and many-to-many associations;

The collection can contain entities that are read-only, as well as entities that are not read-only.

Entities can be added and removed from the collection; changes are flushed to the database.

If automatic versioning is used, Hibernate will update the version due to changes in the collection if they dirty the owning entity.

### 12.2.3. Bidirectional associations

#### 12.2.3.1. Bidirectional one-to-one

If a read-only entity owns a bidirectional one-to-one association:

- Hibernate does not dirty-check the association.

- updates that change the association reference to null or to refer to a different entity will not be flushed to the database.

- If automatic versioning is used, Hibernate will not increment the version due to local changes to the association.

## Note

If an entity is of an immutable class, and it owns a bidirectional one-to-one association, then its reference must be assigned when that entity is first created. Because the entity is automatically made read-only, these references cannot be updated.

When the owner is not read-only, Hibernate treats an association with a read-only entity the same as when the association is with an entity that is not read-only.

#### 12.2.3.2. Bidirectional one-to-many/many-to-one

A read-only entity has no impact on a bidirectional one-to-many/many-to-one association if:

- the read-only entity is on the one-to-many side using an inverse collection;

- the read-only entity is on the one-to-many side using a non-inverse collection;

- the one-to-many side uses a non-inverse collection that contains the read-only entity

When the one-to-many side uses an inverse collection:

- a read-only entity can only be added to the collection when it is created;

- a read-only entity can only be removed from the collection by an orphan delete or by explicitly deleting the entity.

#### 12.2.3.3. Bidirectional many-to-many

Hibernate treats bidirectional many-to-many associations owned by a read-only entity the same as when owned by an entity that is not read-only.

Hibernate dirty-checks bidirectional many-to-many associations.

The collection on either side of the association can contain entities that are read-only, as well as entities that are not read-only.

Entities are added and removed from both sides of the collection; changes are flushed to the database.

If automatic versioning is used, Hibernate will update the version due to changes in both sides of the collection if they dirty the entity owning the respective collections.

- PrevChapter 11. Working with objects
- Up
- Home
- NextChapter 13. Transactions and Concurrency