<?xml version='1.0' encoding="UTF-8"?>

<!--
  ~ Hibernate, Relational Persistence for Idiomatic Java
  ~
  ~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
  ~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
  -->
<chapter xml:id="readonly" xmlns="http://docbook.org/ns/docbook">
    <title>Read-only entities</title>

    <important>
        <para>
            Hibernate's treatment of <emphasis>read-only</emphasis> entities may 
            differ from what you may have encountered elsewhere. Incorrect usage 
            may cause unexpected results.
        </para>
    </important>

    <para>
        When an entity is read-only:

        <itemizedlist>
            <listitem>
                <para> 
                    Hibernate does not dirty-check the entity's simple 
                    properties or single-ended associations;
                </para>
            </listitem>
            <listitem>
                <para> 
                    Hibernate will not update simple properties or updatable
                    single-ended associations; 
                </para>
            </listitem>
            <listitem>
                <para> 
                    Hibernate will not update the version of the read-only 
                    entity if only simple properties or single-ended 
                    updatable associations are changed;
                </para>
            </listitem>
        </itemizedlist>
    </para>

    <para>
        In some ways, Hibernate treats read-only entities the same as entities that are 
        not read-only:
 
        <itemizedlist>
            <listitem>
                <para> 
                    Hibernate cascades operations to associations as
                    defined in the entity mapping.
                </para>
            </listitem>
            <listitem>
                <para> 
                    Hibernate updates the version if the entity has a 
                    collection with changes that dirties the entity;
                </para>
            </listitem>
            <listitem>
                <para> 
                    A read-only entity can be deleted.
                </para>
            </listitem>
        </itemizedlist>
    </para>

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

    <para>
        For details about the affect of read-only entities on different
        property and association types, see 
        <xref linkend="readonly-proptypes"/>.
    </para>

    <para>     
        For details about how to make entities read-only, see 
        <xref linkend="readonly-api"/>
    </para>

    <para>
        Hibernate does some optimizing for read-only entities:
    </para>
        <itemizedlist>
            <listitem>
                <para>
                    It saves execution time by not dirty-checking simple properties or 
                    single-ended associations.
                 </para>
            </listitem>
            <listitem>
                <para>
                    It saves memory by deleting database snapshots.
                </para>   
            </listitem>
        </itemizedlist>

    <section xml:id="readonly-api">
        <title>Making persistent entities read-only</title>

        <para>
            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.
        </para>

        <para>
            Hibernate provides the following ways to make persistent entities read-only:
        </para>
    
        <itemizedlist>
            <listitem>
                <para>
                    you can map an entity class as <emphasis>immutable</emphasis>;
                    when an entity of an immutable class is made persistent,
                    Hibernate automatically makes it read-only.
                    see <xref linkend="readonly-api-immutable"/> for details 
                </para>
            </listitem>
            <listitem>
                <para>
                    you can change a default so that entities loaded
                    into the session by Hibernate are automatically 
                    made read-only; see <xref linkend="readonly-api-loaddefault"/> for details
                </para>
            </listitem>
            <listitem>
                <para>
                    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 <xref linkend="readonly-api-querycriteria"/> for details
                </para>
            </listitem>
            <listitem>
                <para>
                    you can make a persistent entity that is already in the
                    in the session read-only; see 
                    <xref linkend="readonly-api-entity"/> for details
                </para>
            </listitem>
        </itemizedlist>

        <section xml:id="readonly-api-immutable">
            <title>Entities of immutable classes</title>

            <para>
                When an entity instance of an immutable class is made 
                persistent, Hibernate automatically makes it read-only. 
            </para>
            <para>
                An entity of an immutable class can created
                and deleted the same as an entity of a mutable class.
            </para>

            <para>
                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.
            </para>

        </section>

        <section xml:id="readonly-api-loaddefault">
            <title>Loading persistent entities as read-only</title>
 
            <note>
                <para>
                    Entities of immutable classes are automatically loaded
                    as read-only.
                </para>
            </note>
 
            <para>
                To change the default behavior so Hibernate loads entity 
                instances of mutable classes into the session and automatically 
                makes them read-only, call:
            </para>
            <programlisting role="Java">Session.setDefaultReadOnly( true );</programlisting>

            <para>
                To change the default back so entities loaded by Hibernate are not
                made read-only, call:
            </para>
                <programlisting role="Java">Session.setDefaultReadOnly( false );</programlisting>

            <para>
                You can determine the current setting by calling:
            </para>
            <programlisting role="Java">Session.isDefaultReadOnly();</programlisting>

            <para> 
                If Session.isDefaultReadOnly() returns true, entities loaded by 
                the following are automatically made read-only:
            </para>
            <itemizedlist>
                <listitem>
                    <para>
                        Session.load()
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Session.get()
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Session.merge()
                    </para>   
                </listitem>
                <listitem>
                    <para>
                        executing, scrolling, or iterating HQL queries and 
                        criteria; to override this setting for a particular
                        HQL query or criteria see 
                        <xref linkend="readonly-api-querycriteria"/> 
                    </para>   
                </listitem>
            </itemizedlist>

            <para>
                Changing this default has no effect on:
            </para>
            <itemizedlist>
                <listitem>
                    <para>
                        persistent entities already in the session when the 
                        default was changed
                    </para>
                </listitem>
                <listitem>
                    <para>
                        persistent entities that are refreshed via
                        Session.refresh(); a refreshed persistent 
                        entity will only be read-only if it was 
                        read-only before refreshing
                    </para>
                </listitem>
                <listitem>
                    <para>
                        persistent entities added by the application via 
                        Session.persist(), Session.save(), and Session.update()
                        Session.saveOrUpdate()
                    </para>   
                </listitem>
            </itemizedlist>

        </section>

        <section xml:id="readonly-api-querycriteria">
            <title>Loading read-only entities from an HQL query/criteria</title> 

            <note>
                <para>
                    Entities of immutable classes are automatically loaded
                    as read-only.
                </para>
            </note>

           <para>
                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.
            </para>

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

            <para>
                For an HQL query, call:
            </para>
            <programlisting role="Java">Query.setReadOnly( true );</programlisting>

            <para>
                <literal>Query.setReadOnly( true )</literal> must be called before
                <literal>Query.list()</literal>, <literal>Query.uniqueResult()</literal>,
                <literal>Query.scroll()</literal>, or <literal>Query.iterate()</literal>
            </para>

            <para>
                For an HQL criteria, call:
            </para>
            <programlisting role="Java">Criteria.setReadOnly( true );</programlisting>

            <para>
                <literal>Criteria.setReadOnly( true )</literal> must be called before
                <literal>Criteria.list()</literal>, <literal>Criteria.uniqueResult()</literal>,
                or <literal>Criteria.scroll()</literal>
            </para>

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

            <para>
                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.   
            </para>

            <para>
                Using <literal>Query.setReadOnly( true )</literal> or
                <literal>Criteria.setReadOnly( true )</literal> 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.
            </para>

            <para>
                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.
            </para>

            <programlisting role="Java">
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();

</programlisting> 

            <para>
                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.
            </para>

        </section>

        <section xml:id="readonly-api-entity">
            <title>Making a persistent entity read-only</title>
            <note>
                <para>
                    Persistent entities of immutable classes are automatically
                    made read-only.
                </para>
            </note>

            <para>
                To make a persistent entity or proxy read-only, call:
            </para> 
            <programlisting>Session.setReadOnly(entityOrProxy, true)</programlisting> 
 
            <para>
                To change a read-only entity or proxy of a mutable class so 
                it is no longer read-only, call:
            </para>
            <programlisting>Session.setReadOnly(entityOrProxy, false)</programlisting> 

            <important>
                <para>
                    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.
                </para>
            </important>
            
            <para>        
                To throw away non-flushed changes and make the persistent entity 
                consistent with its database representation, call:           </para>
            <programlisting role="Java">session.refresh( entity );</programlisting>

            <para>
                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:
            </para>
<programlisting role="Java">
// 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();
</programlisting>
        </section>
    </section>

    <section xml:id="readonly-proptypes">
        <title>Read-only affect on property type</title>

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

        <table frame="topbot" xml:id="readonly-proptype-summary">
            <title>Affect of read-only entity on property types</title>
            <tgroup cols="2">        
                <colspec colwidth="1*"/>
                <colspec colwidth="1*"/>
                <thead>
                    <row>
                        <entry>Property/Association Type</entry>
                        <entry>Changes flushed to DB?</entry>
                    </row>
                </thead>
                <tbody>
                    <row>
                        <entry>
                            Simple
                            <para>
                                (<xref linkend="readonly-proptypes-simple"/>)
                            </para>
                        </entry>
                        <entry>no*</entry>
                    </row>
                    <row>
                        <entry>
                            <para>Unidirectional one-to-one</para> 
                            <para>Unidirectional many-to-one</para> 
                            <para>
                                (<xref linkend="readonly-proptypes-singleended-unidir"/>)
                            </para>
                        </entry>

                        <entry>
                            <para>no*</para>
                            <para>no*</para>
                        </entry>
                    </row>
                    <row>
                        <entry>
                            <para>Unidirectional one-to-many</para>
                            <para>Unidirectional many-to-many</para>
                            <para>
                                (<xref linkend="readonly-proptypes-manyended-unidir"/>)
                            </para>
                        </entry>
                        <entry>
                            <para>yes</para>
                            <para>yes</para>
                        </entry>
                   </row>
                    <row>
                        <entry>
                            <para>Bidirectional one-to-one</para>
                            <para>
                                (<xref linkend="readonly-proptypes-onetoone-bidir"/>)
                            </para>
                        </entry>
                        <entry>only if the owning entity is not read-only*</entry>
                    </row>
                    <row>
                        <entry>
                            <para>Bidirectional one-to-many/many-to-one</para>
                            <para>inverse collection</para>
                            <para>non-inverse collection</para>
                            <para>
                                (<xref linkend="readonly-proptypes-onetomany-manytoone"/>)
                            </para>
                        </entry>
                        <entry>
                            <para> </para>
                            <para>only added/removed entities that are not read-only*</para>
                            <para>yes</para>
                        </entry>
                    </row>
                    <row>
                        <entry>
                            <para>Bidirectional many-to-many</para>
                            <para>
                                (<xref linkend="readonly-proptypes-manytomany-bidir"/>)
                            </para>
                        </entry>
                        <entry>yes</entry>
                    </row>
                </tbody>
            </tgroup>
        </table>
  
        <para>
            * Behavior is different when the entity having the property/association
              is read-only, compared to when it is not read-only.
        </para>

        <section xml:id="readonly-proptypes-simple">
            <title>Simple properties</title>

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

            <para>
                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.
            </para>

	    <programlisting role="Java">
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();
            </programlisting>

        </section>

        <section xml:id="readonly-prop-types-unidir">
            <title>Unidirectional associations</title>

            <section xml:id="readonly-proptypes-singleended-unidir">
                <title>Unidirectional one-to-one and many-to-one</title>

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

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

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

                <para>
                    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. 
                </para>

                <note>
                    <para>
                        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.
                    </para>
                </note>

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

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

                <para>
                    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.
                </para>

<programlisting role="Java">// 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();</programlisting>

                <para>
                    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.
                </para>

<programlisting role="Java">// 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();</programlisting>

            </section>

            <section xml:id="readonly-proptypes-manyended-unidir">
                <title>Unidirectional one-to-many and many-to-many</title>

                <para>
                    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.
                </para>

                <para>
                    Hibernate dirty-checks unidirectional one-to-many and 
                    many-to-many associations;
                </para>

                <para>
                    The collection can contain entities that 
                    are read-only, as well as entities
                    that are not read-only.
                </para>

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

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

            </section>

        </section>

        <section xml:id="readonly-proptypes-bidir">
            <title>Bidirectional associations</title>

            <section xml:id="readonly-proptypes-onetoone-bidir">
                <title>Bidirectional one-to-one</title>

                <para>
                    If a read-only entity owns a bidirectional 
                    one-to-one association:
                </para>

                <itemizedlist>
                    <listitem>
                        <para>
                            Hibernate does not dirty-check the association. 
                        </para>
                    </listitem>
                    <listitem>
                        <para>
                            updates that change the association reference 
                            to null or to refer to a different entity
                            will not be flushed to the database. 
                        </para>
                    </listitem>
                    <listitem>
                        <para>
                            If automatic versioning is used, Hibernate will not 
                            increment the version due to local changes to 
                            the association.
                       </para>   
                    </listitem>
                </itemizedlist>

                <note>
                    <para>
                        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.
                    </para>   
                </note>

                <para>
                    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.
                </para>

            </section>

            <section xml:id="readonly-proptypes-onetomany-manytoone">
                <title>Bidirectional one-to-many/many-to-one</title>

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

                <itemizedlist>
                    <listitem>
                        <para>
                            the read-only entity is on the one-to-many side
                            using an inverse collection;
                        </para>
                    </listitem>
                    <listitem>
                        <para>
                            the read-only entity is on the one-to-many side
                            using a non-inverse collection;                            
                        </para>
                    </listitem>
                    <listitem>
                        <para>
                            the one-to-many side uses a non-inverse collection 
                            that contains the read-only entity
                       </para>   
                    </listitem>
                </itemizedlist>

                <para>
                    When the one-to-many side uses an inverse collection:
                </para>

                <itemizedlist>
                    <listitem>
                        <para>
                            a read-only entity can only be added to the collection
                            when it is created;
                        </para>
                    </listitem>
                    <listitem>
                        <para>
                            a read-only entity can only be removed from the
                            collection by an orphan delete or by explicitly
                            deleting the entity.                            
                        </para>
                    </listitem>
                </itemizedlist>

            </section>

            <section xml:id="readonly-proptypes-manytomany-bidir">
                <title>Bidirectional many-to-many</title>
                <para>
                    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.
                </para>

                <para>
                    Hibernate dirty-checks bidirectional many-to-many 
                    associations.
                </para>

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

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

                <para> 
                    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.
                </para>

            </section>

        </section>
    </section>
</chapter>
