JPA Implementation Patterns: Field Access Vs. Property Access

JPA Implementation Patterns: Field Access Vs. Property Access

From: http://blog.xebia.com/2009/06/13/jpa-implementation-patterns-field-access-vs-property-access/

Last week my colleague Albert Sikkema blogged about using UUIDs as primary keys. Interesting stuff, thanks again, Albert! This week I will continue the JPA implementation patterns series by discussing the relative merits of field access vs. property access.

The JPA specification allows two ways for the persistence provider to access the persistent state of an entity. The persistence provider can either invoke JavaBeans style property accessors (getters and setters) or access the instance fields of the entity directly. Which method is used depends on whether you have annotated the properties or the fields of an entity.

The JPA 1.0 specification does not allow you to mix access types within an entity or even an entity hierarchy. If you have annotated both fields and properties, the behaviour is undefined. The JPA 2.0 specification has the @Access annotation that makes it possible mix access types within an entity or entity hierarchy.

But the interesting question remains; which access type to use? A question that has been discussed before, but one I couldn’t resist commenting on too.;-)

  • Encapsulation – Property access is said to provide better encapsulation, because directly accessing fields is bad, right? Well actually, using property access obliges you to write getters and setters for all your persistent properties. These methods not only allow the JPA provider to set the fields, they also allow any other user of your entity class to do this! Using field access allows you to write only the getters and setters you want to (they’re evil, remember?) and also write them as you want them, for example by doing validation in your setters or some calculation in your getters. In contrast, making these methods smarter when using property access is just asking for trouble.
  • Performance – Some people prefer field access because it supposedly offers better performance than property access. But that is a very bad reason to choose field access. Modern optimizing JVMs will make property access perform just as fast as field access and in any case database calls are orders of magnitude slower than either field access or method invocations.
  • Lazy loading in Hibernate – Hibernate’s lazy loading implementation always initializes a lazy proxy when any method on that proxy is invoked. The only exception to this is the method annotated with the @Idannotation when you use property access. But when you use field access there is no such method and Hibernate initializes the proxy even when invoking the method that returns the identity of the entity. While some propose to use property access until this bug is fixed, I am not in favour of basing design decisions on framework bugs. If this bug really hurts your performance you might want to try and get the id of entity with the following code:
    Serializable id = ((HibernateProxy) entity).getHibernateLazyInitializer().getIdentifier()
    

    It’s nasty, but at least this code will be localized to where you really need it.

  • Field access in Hibernate – It is good to know that while field access is OK for Hibernate to populate your entities, your code should still access those values through methods. Otherwise you will fall into the first of the Hibernate proxy pitfalls mentioned by my colleague Maarten Winkels.

To summarize I think field access is the way to go because it offers better encapsulation (without itproperly managing bidirectional associations is impossible) and the performance impact is negligible (#1 on the performance problems top 10 is still the interplay between the database and your Java app). The only downside are some snafu’s in Hibernate’s lazy loading implementation that require you to take extra care when using field access.

What access type do you prefer? Do you see any difference in the way field access and property access are implemented in JPA providers other than Hibernate? Please let me know by leaving a comment below. See you at the next JPA implementation patterns blog in which I will talk about mapping inheritance hierarchies in JPA.

For a list of all the JPA implementation pattern blogs, please refer to the JPA implementation patterns wrap-up.

Forcefully initializing proxies in Hibernate

Forcefully initializing proxies in Hibernate

As we have seen, The Hibernate Entities that we load in a persistence context may be made of proxies and collection wrappers. The simplest way to initialize these proxies would be to call any of the data getter methods (except Identifier).
Consider that you have loaded an object. You now need to detach the object and return it to a different function outside the persistence context.

public static void test() {
    Session session = sessionFactory.openSession();
    System.out.println("Loading a shelf object");
    Shelf shelf = (Shelf) session.load(Shelf.class, shelfId);
    session.close();
    useDetached(shelf);
}
    
public static void useDetached(Shelf shelf) {
    //do operations here
    System.out.println("Shelf code is " + shelf.getCode());
}

The above code results in the infamous exception

Exception in thread "main" org.hibernate.LazyInitializationException: could not
initialize proxy - no Session

The way around would be to initialize the entities needed.

public static void test() {
    Session session = sessionFactory.openSession();
    System.out.println("Loading a shelf object");
    Shelf shelf = (Shelf) session.load(Shelf.class, shelfId);
    shelf.getCode();// this will load the object
    session.close();
    useDetached(shelf);
}

This will make the code work, but looks very stupid and will frankly cause some other developer to curse you one day. Hibernate provides a better method to initialize objects. Hibernate.initialize()

public static void testInitializeProxy() {
    Session session = sessionFactory.openSession();
    System.out.println("Loading a shelf object");
    Shelf shelf = (Shelf) session.load(Shelf.class, shelfId);
    System.out.println("calling Hibernate.initialize() on the shelf");
    Hibernate.initialize(shelf);
    System.out.println("executed Hibernate.initialize() on shelf");
    session.close();
}

The logs are as below:

Loading a shelf object
calling Hibernate.initialize() on the shelf
Hibernate: 
    /* load com.collection.smart.Shelf */
     select
        shelf0_.ID as ID0_0_,
        shelf0_.CODE as CODE0_0_ 
    from
        SHELF shelf0_ 
    where
        shelf0_.ID=?
executed Hibernate.initialize() on shelf

This method is a very rare necessity. To lazily initialize a collection:

public static void testInitializeCollection() {

    Session session = sessionFactory.openSession();
    Shelf shelf = (Shelf) session.load(Shelf.class, shelfId);
    Set<Book> books = shelf.getAllBooks();
    System.out.println("calling Hibernate.initialize() on set ");
    Hibernate.initialize(books);
    System.out.println("executed Hibernate.initialize() on the set ");
    session.close();
    for(Book book: books) {
        System.out.println("Book class is " + book.getClass());
    }    
}

The logs indicate the output:

Hibernate: 
    /* load com.collection.smart.Shelf */
    select
        shelf0_.ID as ID0_0_,
        shelf0_.CODE as CODE0_0_ 
    from
        SHELF shelf0_ 
    where
        shelf0_.ID=?
calling Hibernate.initialize() on set 
Hibernate: 
    /* load one-to-many com.collection.smart.Shelf.allBooks */
     select
        allbooks0_.SHELF_ID as SHELF3_1_,
        allbooks0_.ID as ID1_,
        allbooks0_.ID as ID1_0_,
        allbooks0_.Name as Name1_0_,
        allbooks0_.shelf_id as shelf3_1_0_ 
    from
        BOOK allbooks0_ 
    where
        allbooks0_.SHELF_ID=?
executed Hibernate.initialize() on the set 
Book class is class com.collection.smart.Book
Book class is class com.collection.smart.Book

For the collection scenario the java docs comes with a warning:
Note: This only ensures initialization of a proxy object or collection; it is not guaranteed that the elements INSIDE the collection will be initialized/materialized. 
In our case, as the SQL Query was executed the records were initialized successfully. I even used a smart collection to see if the entities would be loaded. However in that case too, on calling the initialize method Hibernate loaded all the Book entities and not Book proxies.

Select in Criteria using SQL, Hibernate

Select in Criteria using SQL

In an earlier post we saw how Criteria provided us with the ability to add restrictions in the where clause using pure SQL. The next question would then be if Criteria allows SQL in select clause ?
I tested the below code:

public static void testSqlSelect() {
    final Session session = sessionFactory.openSession();
    Criteria criteria = session.createCriteria(Entity.class);
    int id = 4;
    criteria.add(Restrictions.eq("id", id));
    criteria.setProjection(Projections
        .projectionList()
            .add(Projections.property("name"))
            .add(Projections.property("master"))
            .add(Projections
                .sqlProjection(
                    "(select count(c.id) from CHILD_ENTITY c INNER JOIN ENTITY e on c.ENTITY_ID = e.id "
                    + "where e.id = " + id + ") as childCount",
                    new String[] { "childCount" },
                    new Type[] { Hibernate.LONG }
                )
            )
        );
    List<Object[]> rows = criteria.list();
    for (Object[] row : rows) {
        System.out.println(" Entity is " + row[0] + " and " + row[1] + " no of kids : " + row[2]);
    }
    session.close();
}

In this the normal Criteria returns me the details of Entity and its Master. But I have added a separate SQL query that will return me the number of ChildEntities associated with the particular record. The output of the code is as below:

    select
        this_.NAME as y0_,
        this_.MASTER_ID as y1_,
        (select
            count(c.id) 
        from
            CHILD_ENTITY c 
        INNER JOIN
            ENTITY e 
                on c.ENTITY_ID = e.id 
        where
            e.id = 4) as childCount 
    from
        ENTITY this_ 
    where
        this_.ID like ?
Hibernate: 
    /* load com.model.Master */ select
        master0_.ID as ID2_0_,
        master0_.DATA as DATA2_0_ 
    from
        ENTITY_MASTER master0_ 
    where
        master0_.ID=?
 Entity is entity100 and [Master] : ( id 2 , data : master No 2 )] no of kids : 1

As can be seen the SQL query was embedded within the select clause. The query being SQL, it could refer to any table or column whether known or unknown to Hibernate. The other parameters are used to update Hibernate with details of the return type from the statement. As it is an array, the inner select is capable of returning multiple columns.
An important thing to keep in mind though is that the sub-query is not allowed to return more than one row. This will result in an SQL exception if my entity row has more than one Entity_Child associations:

    select
        this_.NAME as y0_,
        this_.MASTER_ID as y1_,
        (select
c.id 
        from
            CHILD_ENTITY c 
        INNER JOIN
            ENTITY e 
                on c.ENTITY_ID = e.id 
        where
            e.id = 2) as childCount 
    from
        ENTITY this_ 
    where
        this_.ID=?

Source: http://learningviacode.blogspot.com/2013/04/select-in-criteria-using-sql.html

Creating a new Criterion, Hibernate

Creating a new Criterion

We have seen Hibernate’s set of Restrictions and they cover a very large range of queries. However Hibernate also allows the creation of a custom Criterion that can be used in the criteria.
I created a simple Criterion class that is used to check the length of string properties.

@SuppressWarnings("serial")
public class LengthExpression implements Criterion {
    private final String propertyName;
    private final int value;

    public LengthExpression(final String propertyName, final int value) {
        this.propertyName = propertyName;
        this.value = value;
    }

    @Override
    public String toSqlString(final Criteria criteria,
            final CriteriaQuery criteriaQuery) throws HibernateException {
        criteriaQuery.getFactory().getDialect();
        final String[] columns = criteriaQuery.getColumnsUsingProjection(
                criteria, this.propertyName);
        final String queryFragment = "length(" + columns[0] + ") = ?";
        return queryFragment;
    }

    @Override
    public TypedValue[] getTypedValues(final Criteria criteria,
            final CriteriaQuery criteriaQuery) throws HibernateException {
        // An ordered pair of a value and its Hibernate type
        return new TypedValue[] { new TypedValue(Hibernate.INTEGER,
                Integer.valueOf(value), EntityMode.POJO) };
    }
}

In the above code:

  • The class implements the Criterion Interface which exposes two methods.
  • The  toSqlString() method is what generates the actual sql fragment. As we work with prepared statements, “?” ares used. In this case the sql length function is used. The alias used for property in the actual query is obtained using the getColumnsUsingProjection() method.
  • The getTypedValues() method tells Hibernate about the data Type to be used in the query. In this case the parameter will be set using the preparedStatement.setInt() method.

To test the code, I used it in a criteria example:

public static void testCustomCriterion() {
    final Session session = sessionFactory.openSession();
    Criteria criteria = session.createCriteria(Entity.class);
    criteria.add(new LengthExpression("name", 7));        
    List<Entity> entities = criteria.list(); 
    System.out.println(entities);
}

The result of the execution is below:

3484 [main] DEBUG org.hibernate.SQL  - 
    /* criteria query */ 
    select
        this_.ID as ID0_0_,
        this_.NAME as NAME0_0_,
        this_.DATE as DATE0_0_,
        this_.MASTER_ID as MASTER4_0_0_ 
    from
        ENTITY this_ 
    where
        length(this_.NAME) = ?
...
3484 [main] DEBUG org.hibernate.jdbc.AbstractBatcher  - preparing statement
3516 [main] DEBUG org.hibernate.type.IntegerType  - binding '7' to parameter: 1
...
3625 [main] DEBUG org.hibernate.connection.DriverManagerConnectionProvider  - re
turning connection to pool, pool size: 1
[[Entity] : ( id 2 , data : entity1 , master.Id : 1 , date : null )], [Entity] :
 ( id 3 , data : entity2 , master.Id : 1 , date : null )]]

As can be seen the query generated includes the length() function. Also the parameter(7) was bound to the prepared statement.

Native SQL inside Criteria, Hibernate

SQL inside Criteria

We earlier how a custom criterion could be created to handle special scenarios. However this may seem overkill if the condition can be easily expressed using native SQL. For Criteria allows us to add custom SQL expressions which get added in the where clause.
I took the same scenario as the custom criteria – checking the length of a string column:

public static void testViaSql() {
    final Session session = sessionFactory.openSession();
    Criteria criteria = session.createCriteria(Entity.class);
    criteria.add(Restrictions.sqlRestriction(
            "length({alias}.name)< ?", 5, Hibernate.INTEGER)); 
    List<Entity> entities = criteria.list();
    System.out.println(entities);
}

The result is :

select
        this_.ID as ID0_0_,
        this_.NAME as NAME0_0_,
        this_.DATE as DATE0_0_,
        this_.MASTER_ID as MASTER4_0_0_ 
    from
        ENTITY this_ 
    where
        length(this_.name)< ?

As can be seen the restriction was added to the generated SQL query – with one tiny change: The {alias} parameter was replaced with the alias used for the table Entity.
The {alias} here represents the root class – the class for which the criteria is created. Also the “name” used in the SQL string represents the column name and not the class property. (This is SQL remember.)
The Restrictions.sqlRestriction method also has two more parameters – an object that will be substituted for the question mark. The third is the Hibernate built-in type. In this case as our parameter is an integer, we pass the INTEGER type.
What if we do not have any parameters ?

public static void testViaSql_Simpler() {
    final Session session = sessionFactory.openSession();
    Criteria criteria = session.createCriteria(Entity.class);
    criteria.add(Restrictions.sqlRestriction("{alias}.master_id = 2"));
    List<Entity> entities = criteria.list();
    System.out.println(entities);
}

As can be seen there is an overloaded version that allows for just the where condition to be specified – without any parameters.
This facility can be used to create much more complex queries too.

public static void testViaSql_Complex() {
    final Session session = sessionFactory.openSession();
    Criteria criteria = session.createCriteria(Entity.class);
    criteria.add(Restrictions
            .sqlRestriction("{alias}.master_id = "
                    + "(select id from entity_master where data like 'master No 1')"));
    List<Entity> entities = criteria.list();
    System.out.println(entities);
}

The SQL is:

select
        this_.ID as ID0_0_,
        this_.NAME as NAME0_0_,
        this_.DATE as DATE0_0_,
        this_.MASTER_ID as MASTER4_0_0_ 
    from
        ENTITY this_ 
    where
        this_.master_id = (
            select
                id 
            from
                entity_master 
            where
                data like'master No 1'
        )

In this case we used it to fire nested queries. The subquery feature is also available directly in the Criteria API and we shall see that in the next post.

Hibernate 4 Util SessionFactory example


import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil
{
    private static SessionFactory sessionFactory;
    private static ServiceRegistry serviceRegistry;

    static
    {
        try
        {
//          Configuration configuration = new Configuration();
            Configuration configuration = new Configuration().configure();

            serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
            sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        }
        catch (HibernateException he)
        {
            System.err.println("Error creating Session: " + he);
            throw new ExceptionInInitializerError(he);
        }
    }

    public static SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }
}

Credits: http://stackoverflow.com/questions/8621906/is-buildsessionfactory-deprecated-in-hibernate-4