0% found this document useful (0 votes)
154 views

Hibernate Interview Questions and Answers

Hibernate is a popular Java framework for object-relational mapping. It can be configured using hibernate.cfg.xml or hibernate.properties files. Mapping files define how Java objects map to database tables. The SessionFactory is a thread-safe object that manages Sessions. Sessions are lightweight objects that represent a single unit of work and are not thread-safe. Objects transition between persistent, detached, and transient states during their lifecycle. The second-level cache improves performance by caching objects between Sessions.

Uploaded by

Deepak
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
154 views

Hibernate Interview Questions and Answers

Hibernate is a popular Java framework for object-relational mapping. It can be configured using hibernate.cfg.xml or hibernate.properties files. Mapping files define how Java objects map to database tables. The SessionFactory is a thread-safe object that manages Sessions. Sessions are lightweight objects that represent a single unit of work and are not thread-safe. Objects transition between persistent, detached, and transient states during their lifecycle. The second-level cache improves performance by caching objects between Sessions.

Uploaded by

Deepak
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 51

Hibernate Interview Questions and Answers: Overview

If you ask me to list two frameworks that are very prevalently used in Java projects and
very popular in job interviews, it would be (1) Spring framework (2)
Hibernate framework.

Q. How will you configure Hibernate?


A. The configuration files hibernate.cfg.xml (or hibernate.properties) and mapping files
*.hbm.xml are used by the Configuration class to create (i.e. configure and bootstrap
hibernate) the SessionFactory, which in turn creates the Session instances. Session
instances are the primary interface for the persistence service.

hibernate.cfg.xml (alternatively can use hibernate.properties): These two files are


used to configure the hibernate sevice (connection driver class, connection URL,
connection username, connection password, dialect etc). If both files are present in the
classpath then hibernate.cfg.xml file overrides the settings found in the
hibernate.properties file.

Mapping files (*.hbm.xml): These files are used to map persistent objects to a relational
database. It is the best practice to store each object in an individual mapping file (i.e
mapping file per class) because storing large number of persistent classes into one
mapping file can be difficult to manage and maintain. The naming convention is to use
the same name as the persistent (POJO) class name. For example Account.class will have
a mapping file named Account.hbm.xml. Alternatively, hibernate annotations can be used
as part of your persistent class code instead of the *.hbm.xml files.

Q. What is a SessionFactory? Is it a thread-safe object?


A. SessionFactory is Hibernate's concept of a single datastore and is threadsafe so that
many threads can access it concurrently and request for sessions and immutable cache of
compiled mappings for a single database. A SessionFactory is usually only built once at
startup. SessionFactory should be wrapped in some kind of singleton so that it can be
easily accessed in an application code.

SessionFactory sessionFactory = new


Configuration( ).configure( ).buildSessionfactory( );
Q. What is a Session? Can you share a session object between different
threads?
A. Session is a light weight and a non-threadsafe object (No, you cannot share it between
threads) that represents a single unit-of-work with the database. Sessions are opened by a
SessionFactory and then are closed when all work is complete. Session is the primary
interface for the persistence service. A session obtains a database connection lazily (i.e.
only when required). To avoid creating too many sessions ThreadLocal class can be used
as shown below to get the current session no matter how many times you make call to the
currentSession( ) method.

view plainprint?
1. public class HibernateUtil {
2.
3. public static final ThreadLocal local = new ThreadLocal();
4.
5. public static Session currentSession() throws HibernateException {
6. Session session = (Session) local.get();
7. //open a new session if this thread has no session
8. if(session == null) {
9. session = sessionFactory.openSession();
10. local.set(session);
11. }
12. return session;
13. }
14. }

It is also vital that you close your session after your unit of work completes.

Note: Keep your Hibernate Session API handy. Quite often, hibernate is used with Spring
framework, using the Template design pattern.
Q. Explain hibernate object states? Explain hibernate objects life cycle?
A.

Persistent objects and collections are short lived single


threaded objects, which store the persistent state. These
objects synchronize their state with the database depending on
your flush strategy (i.e. auto-flush where as soon as setXXX()
method is called or an item is removed from a Set, List etc or
define your own synchronization points with session.flush(),
transaction.commit() calls). If you remove an item from a
persistent collection like a Set, it will be removed from the
database either immediately or when flush() or commit() is
called depending on your flush strategy. They are Plain Old
Java Objects (POJOs) and are currently associated with a
session. As soon as the associated session is closed, persistent
objects become detached objects and are free to use directly as
data transfer objects in any application layers like business
layer, presentation layer etc.

Detached objects and collections are instances of persistent


objects that were associated with a session but currently not
associated with a session. These objects can be freely used as
Data Transfer Objects without having any impact on your
database. Detached objects can be later on attached to another
session by calling methods like session.update(),
session.saveOrUpdate() etc. and become persistent objects.

Transient objects and collections are instances of persistent


objects that were never associated with a session. These
objects can be freely used as Data Transfer Objects without
having any impact on your database. Transient objects become
persistent objects when associated to a session by calling
methods like session.save( ), session.persist( ) etc.
Q. What are the benefits of detached objects?
A.

Pros:
 When long transactions are required due to user think-time, it is the best practice
to break the long transaction up into two or more transactions. You can use
detached objects from the first transaction to carry data all the way up to the
presentation layer. These detached objects get modified outside a transaction and
later on re-attached to a new transaction via another session.

Cons:
 In general, working with detached objects is quite cumbersome, and it is better
not to clutter up the session with them if possible. It is better to discard them and
re-fetch them on subsequent requests. This approach is not only more portable but
also more efficient because - the objects hang around in Hibernate's cache
anyway.

 Also from pure rich domain driven design perspective, it is recommended to use
DTOs (DataTransferObjects) and DOs (DomainObjects) to maintain the
separation between Service and UI tiers.

Q. When does an object become detached?


A.

view plainprint?
1. Session session1 = sessionFactory.openSession();
2. Car myCar = session1.get(Car.class, carId);//”myCar” is a persistent object at this
stage.
3. session1.close(); //once the session is closed “myCar” becomes a detached o
bject

you can now pass the “myCar” object all the way upto the presentation tier. It can be
modified without any effect to your database table.

view plainprint?
1. myCar.setColor(“Red”); //no effect on the database

When you are ready to persist this change to the database, it can be reattached to another
session as shown below:

view plainprint?
1. Session session2 = sessionFactory.openSession();
2. Transaction tx = session2.beginTransaction();
3. session2.update(myCar); //detached object ”myCar” gets re-attached
4. tx.commit(); //change is synchronized with the database.
5. session2.close()

Q. How does Hibernate distinguish between transient (i.e. newly instantiated) and
detached objects?
A.
 Hibernate uses the "version" property, if there is one.
 If not uses the identifier value. No identifier value means a new object. This does
work only for Hibernate managed surrogate keys. Does not work for natural keys
and assigned (i.e. not managed by Hibernate) surrogate keys.
 Write your own strategy with Interceptor.isUnsaved( ).

Note: When you reattach detached objects, you need to make sure that the dependent
objects are reattached as well.

Note: Extracted from my book "Java/J2EE Job Interview Companion".

Hibernate Interview questions and answers: cacheing

Q. What is a second-level cache in Hibernate?


A. Hibernate uses two different caches for objects: first-level cache and second-level
cache. First-level cache is associated with the Session object, while second-level cache is
associated with the SessionFactory object. By default, Hibernate uses first-level cache on
a per-transaction basis. Hibernate uses this cache mainly to reduce the number of SQL
queries it needs to generate within a given transaction. For example, if an object is
modified several times within the same transaction, Hibernate will generate only one
SQL UPDATE statement at the end of the transaction, containing all the modifications.
The second-level cache needs to be explicitly configured. Hibernate provides a flexible
concept to exchange cache providers for the second-level cache. By default Ehcache is
used as caching provider. However more sophisticated caching implementation can be
used like the distributed JBoss Cache or Oracle Coherence.

The Hibernate configuration looks like:

?
<property name="hibernate.cache.use_second_level_cache">true</property>
1 <property
2 name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

The ehcache.xml can be configured to cache objects of type com.myapp.Order as shown


below

?
1 <cache name="com.myapp.Order"
maxElementsInMemory="300"
2
eternal="true"
3 overflowToDisk="false"
4 timeToIdleSeconds="300"
5 timeToLiveSeconds="300"
6 diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
7
memoryStoreEvictionPolicy="LRU"
8
9 />
10

second-level cache reduces the database traffic by caching loaded objects at the
SessionFactory level between transactions. These objects are available to the whole
application, not just to the user running the query. The 'second-level' cache exists as long
as the session factory is alive. The second-level cache holds on to the 'data' for all
properties and associations (and collections if requested) for individual entities that are
marked to be cached. It is imperative to implement proper cache expiring strategies as
caches are never aware of changes made to the persistent store by another application. he
following are the list of possible cache strategies.

 Read-only: This is useful for data that is read frequently, but never updated. This
is the most simplest and best-performing cache strategy.

 Read/write: Read/write caches may be appropriate if your data needs to be


updated. This carry more overhead than read-only caches. In non-JTA
environments, each transaction should be completed when session.close() or
session.disconnect() is called.

 Nonstrict read/write: This is most appropriate for data that is read often but only
occasionally modified.This strategy does not guarantee that two transactions
won't simultaneously modify the same data.

 Transactional: This is a fully transactional cache that may be used only in a JTA
environment.

It can be enabled via the Hibernate mapping files as shown below:

?
1 <class name="com.myapp.Order">
2
3 <cache usage="read-write"/>
4 ....
</class>
5

Note: The usage options are: transactional|read-write|nonstrict-read-write|read-only. The


cache can also be enabled at different granular level (e.g. parent, children, etc). The active
orders will be cached for 300 seconds.

Q. How does the hibernate second-level cache work?


A. Hibernate always tries to first retrieve objects from the session and if this fails it tries
to retrieve them from the second-level cache. If this fails again, the objects are directly
loaded from the database. Hibernate's static initialize() method, which populates a proxy
object, will attempt to hit the second-level cache before going to the database. The
Hibernate class provides static methods for manipulation of proxies.

?
1 public final class Hibernate extends Object {
2 ....
3 public static void initialize(Object proxy) throws HibernateException
4 ....
}
5

As a consequence of using the Hibernate second-level cache, you have to be aware of the
fact that each call of a data access method can either result in a cache hit or miss. So,
configure your log4j.xml to log your hits and misses.

?
1 <logger name="org.hibernate.cache">
2 <level value="DEBUG" />
3 </logger>

Alternatively, you can use Spring AOP to log the cache access on your DAO methods.

The second level cache is a powerful mechanism for improving performance and
scalability of your database driven application. Read-only caches are easy to handle,
while read-write caches are more subtle in their behavior. Especially, the interaction with
the Hibernate session can lead to unwanted behavior.

Q. What is a query cache in Hibernate?


A. The query cache is responsible for caching the results and to be more precise the keys
of the objects returned by queries. Let us have a look how Hibernate uses the query cache
to retrieve objects. In order to make use of the query cache we have to modify the person
loading example as follows.

?
1 Query query = session.createQuery("from Order as o where o.status=?");
2 query.setInt(0, "Active");
3 query.setCacheable(true); // the query is cacheable
4 List l = query.list();

You also have to change the hibernate configuration to enable the query cache. This is
done by adding the following line to the Hibernate configuration.
<property name="hibernate.cache.use_query_cache">true</property>

Q. What are the pitfalls of second level and query caches?


A. Memeory is a finite resource, and over use or incorrect useage like cacheing the Order
object and all its referenced objects can cause OutOfMemoryError. Here are some tips to
overcome the pitfalls relating to cacheing.

1. Set entity’s keys as query parameters, rather than setting the entire entity object.
Critreia representations should also use identifiers as parameters. Write HQL queries to
use identifiers in any substitutable parameters such as WHERE clause, IN clause etc.

In the example below, the entire customer and everything he/she references would be
held in cache until either the query cache exceeds its configured limits and it is evicted,
or the table is modified and the results become dirty.

?
1 final Customer customer = ... ;
2 final String hql = "FROM Order as order WHERE order.custOrder = ?"
3 final Query q = session.createQuery(hql);
4 q.setParameter(0, customer);
q.setCacheable(true);
5

Instead of setting the whole customer object as shown above, just set the id.

?
1 final Order customer = ... ;
2 final String hql = "from Order as order where order.cusomer.id = ?"
3 final Query q = session.createQuery(hql);
4 q.setParameter(0, customer.getId());
q.setCacheable(true);
5

2. Hibernate's query cache implementation is pluggable by decorating Hibernate's query


cache implementation. This involves overriding the put( ) method to check if a canonical
equivalent of a query results object already exist in the Object[][], and assign the same
QueryKey if it exists.

3. If you are in a single JVM using in memory cache only, use


hibernate.cache.use_structured_entries=false in your hibernate configuration.

Here are some general performance tips:

1. Session.load will always try to use the cache. Session.find does not use the cache for
the primary object, but cause the cache to be populated. Session.iterate always uses the
cache for the primary object and any associated objects.

2. While developing, enable the show SQL and monitor the generated SQL.

<property name="show_sql">true</property>

Also enable the "org.hibernate.cache" logger in your log4j.xml to monitor cache hits
and misses.

pring Interview Questions and Answers: Hibernate integration

Q. How would you integrate Hibernate with Spring?


A. The hibernate can be wired up via Spring as shown below.

STEP 1: The Hibernate properties.

?
1
2 <!-- hibernate properties-->
3 <bean id="myappHibernateProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
4 <property name="properties">
5 <props>
6 <prop key="hibernate.dialect">org.hibernate.dialect.SybaseDialect</pr
7 <prop key="hibernate.show_sql">false</prop>
<prop
8
key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvide
9 </props>
10 </property>
11 </bean>

STEP 2: The DataSource.

?
1
2
3 <!-- datasource configured via JNDI-->
4 <bean id="myappDataSource"
5 class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
6 <value>java:comp/env/jdbc/dataSource/myapp-ds</value>
7 </property>
8 </bean>

STEP 3: The hibernate mapping files. The mapping file tells Hibernate what table in the
database it has to access, and what columns in that table it should use.

?
1
2
3 <bean name="myappHibernateMappingFiles"
4 class="org.springframework.beans.factory.config.ListFactoryBean">
5 <property name="sourceList">
6 <<span class="IL_AD" id="IL_AD6">list</span>>
<value>hbm/Order.hbm.xml</value>
7 <value>hbm/Trade.hbm.xml</value>
8 <value>hbm/Customer.hbm.xml</value>
9 </list>
10 </property>
</bean>
11

STEP 4: An empty interceptor

?
1
2 <!-- An interceptor that does nothing. May be used as a base class
3 for application-defined custom interceptors. -->
4 <bean id="myappEntityInterceptor"
class="org.hibernate.EmptyInterceptor" />

STEP 5: Usually an application has a single SessionFactory instance and threads


servicing client requests obtain Session instances from this factory.

?
1
2
3 <!-- Define the session factory -->
4 <bean name="myappSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
5 autowire="byName">
6 <property name="hibernateProperties">
7 <ref bean="myappHibernateProperties"/>
8 </property>
<property name="dataSource">
9 <ref bean="myappDataSource"/>
10 </property>
11 <property name="mappingResources">
12 <ref bean="myappHibernateMappingFiles"/>
13 </property>
<property name="entityInterceptor">
14 <ref bean="myappEntityInterceptor"/>
15
16 </property>
17 </bean>

STEP 6: The hibernate template. Helper class that simplifies Hibernate data access code.

?
1
2
3 <!-- Hibernate Template -->
4 <bean name="myappHibernateTemplate"
5 class="org.springframework.orm.hibernate3.HibernateTemplate"
6 autowire="no" scope="prototype">
<property name="sessionFactory">
7
<ref bean="myappSessionFactory"/>
8 </property>
9 <property name="allowCreate">
10 <value>false</value>
11 </property>
<property name="maxResults">
12 <value>50000</value>
13 </property>
14 <property name="flushMode">
15 <value>3</value>
16 </property>
</bean>
17

STEP 7: The repository class for data access logic.

1
2 <!-- Constructor Inject the Template into your Repository classes
3 (Data acess layer) -->
<bean id="myappOrderRepository"
4 class="com.HibernateOrderRepository">
5 <constructor-arg><ref bean="myappHibernateTemplate"
6 /></constructor-arg>
</bean>

STEP 8: The service class that uses one or more repositories.

?
1
2
3 <!-- Service Layer: myappOrderRepository is injected via setter
4 injection -->
<bean id="myappOrderService" class="com.OrderServiceImpl">
5 <property><ref bean="myappOrderRepository" /></property>
6 </bean>
7

STEP 9: The repository interface and class that makes use if the hibernate template.

?
1
2 package com;
3
4 import com.ObjectNotFoundException;
5
6 public interface OrderRepository {
7 public Order load(Long identity) throws ObjectNotFoundException;
public void save(Order order);
8
}
9

The template.load, template.save, etc are database operations via the HibernateTemplate.

?
1
2 package com;
3
4 import org.apache.log4j.Logger;
import
5 org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException;
6 import org.springframework.orm.hibernate3.HibernateTemplate;
7
8 import com.ObjectNotFoundException;
9 import com.Order;
10 import com.OrderRepository;
11
12 public class HibernateOrderRepository implements OrderRepository {
13
14 private static final Logger LOG =
15 Logger.getLogger(HibernateOrderRepository.class);
16
17 private final HibernateTemplate template;
18
19
20 public HibernateOrderRepository(HibernateTemplate template) {
this.template = template;
21 }
22
23 @Override
24 /**
25 * Read Order from database
26
27
28 */
29 public Order load(Long identity) throws ObjectNotFoundException {
30 if (LOG.isDebugEnabled()) {
LOG.debug("Loading " + identity);
31
}
32 try {
33 return template.load(Order.class,identity);
34 } catch (HibernateObjectRetrievalFailureException e) {
35 throw new ObjectNotFoundException(identity + " not found", e);
}
36 }
37
38 /**
39 * Save the record to database
40 * @param order */
41 public void save(Order order) {
if (LOG.isDebugEnabled()) {
42 LOG.debug("Saving " + order);
43 }
44 template.save(order);
45 }
46 }
47
48

Q: How do you wire up a transaction manager?


A:

STEP 1: Define a transaction manager.

?
1 <!-- Transaction Manger -->
<bean name="myappTransactionManager"
2 class="org.springframework.orm.hibernate3.HibernateTransactionManager">
3 <property name="sessionFactory" ref="myappSessionFactory" />
4 </bean>

STEP 2: Create a transaction interceptor that uses the above transaction manager.

?
1 <bean id="myappHibernateInterceptor"
2 class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="myappTransactionManager" />
3 <property name="transactionAttributes">
4 <props>
5 <prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
6 </props>
7 </property>
8 </bean>

STEP 3: Create other optional interceptors for logging, deadlock retry, etc.

?
<!-- SERVICE INTERCEPTORS -->
1
2 <bean id="myappLoggingInterceptor"
3 class="com.MyAppLoggingInterceptor" />
4 <bean id="myappDeadlockRetryInterceptor"
class="com.OracleDeadlockRetryInterceptor" />

STEP 4: Wire up the interceptors to an abstract service

?
1 <!-- SERVICES -->
2
3 <bean id="myappAbstractService" abstract="true"
4 class="org.springframework.aop.framework.ProxyFactoryBean">
5 <property name="interceptorNames">
<list>
6 <value>myappLoggingInterceptor</value>
7 <value>myappDeadlockRetryInterceptor</value>
8 <value>myappHibernateInterceptor</value>
9 </list>
10 </property>
</bean>
11

STEP 5: The concrete service that uses the myappOrderRepository along with the
interceptors. Especially the "myappHibernateInterceptor" that performs transaction
demarcation.

?
1 <bean id="myappOrderService" parent="myappAbstractService">
2 <property name="proxyInterfaces">
3 <value>com.mgl.mts.oms.model.service.OrderService</value>
4 </property>
<property name="target">
5 <bean class="com.OrderServiceImpl">
6 <constructor-arg><ref bean="myappOrderRepository"
7 /></constructor-arg>
8 </bean>
9 </property>
</bean>
10
STEP 6 The OrderServiceImpl class looks like

?
1 package com;
2
3 import ....
4
5 public class OrderServiceImpl implements OrderService {
6
7 private final Logger LOG =
Logger.getLogger(OrderServiceImpl.class);
8
9 private final OrderRepository orderRepository;
10
11 public OrderServiceImpl(OrderRepository orderRepository) {
12 this.orderRepository = orderRepository;
13 }
14
15 //......
}
16
Note: The OrderServiceImpl will have the interceptors turned using AOP to manage
transaction, logging, and deadlock retry. The diagram below gives a big picture of
interceptors, service, and repository.
Note: The deadlock retry filter is an interesting one. When an exception is thrown, it is
inspected using a pattern matching (i.e. regular expression) to see if it is due to deadlock.
If it is due to deadlock the invocation is repeated. This makes the call again to the target,
which is the OrderServiceimpl via the TransactionInterceptor, which starts a new
transaction.

Here is an example of the custom interceptor -- myappLoggingInterceptor. Aspect-


Oriented Programming (AOP) offers a better solution to many problems. he AOP
Alliance project (aopalliance-x.x.jar) is a joint open-source project between several
software engineering people who are interested in AOP and Java.

?
1 package com.test;
2
3 import org.aopalliance.intercept.MethodInterceptor;
4 import org.aopalliance.intercept.MethodInvocation;
5 import org.apache.log4j.Logger;
6
7
8 public class MyAppLoggingInterceptor implements MethodInterceptor {
9
private static final Logger LOG =
10 Logger.getLogger(MyAppLoggingInterceptor.class);
11
12 @Override
13 public Object invoke(MethodInvocation invocation) throws Throwable
14 {
long begin = System.currentTimeMillis();
15
16 //proceed to the next interceptor on the chain
17 Object result = invocation.proceed();
18
19 long end = System.currentTimeMillis();;
20
21 LOG.info("Time elapsed " + (end - begin) + " ms");
22
23 return result;
}
24
25
}
26

The deadlock retry interceptor

?
1 package com.test;
2
3 import java.sql.SQLException;
4
import org.aopalliance.intercept.MethodInterceptor;
5 import org.aopalliance.intercept.MethodInvocation;
6 import org.apache.log4j.Logger;
7
8
9 public class OracleDeadlockRetryInterceptor implements
10 MethodInterceptor {
11
12 private static final Logger LOG =
13 Logger.getLogger(OracleDeadlockRetryInterceptor.class);
14
private int attempts = 3;
15
16 @Override
17 public Object invoke(MethodInvocation invocation) throws Throwable
18 {
19 return doInvoke(invocation, 1);
}
20
21
private Object doInvoke(MethodInvocation invocation, int count)
22 throws Throwable {
23 try {
24 //proceed to next interceptor
25 return invocation.proceed();
} catch (Exception exception) {
26 if (!isDeadlockException(exception)) {
27 throw exception;
28 }
29 LOG.warn("A Database deadlock occured. Will try again.",
30 exception);
if (count < attempts) {
31 count++;
32 return doInvoke(invocation, count);
33 }
34 throw new SQLException("Service Invocation failed " +
attempts
35
+ " times with a SQLException.", exception);
36 }
37 }
38
39 }

Hibernate Interview Questions and Answers: with annotations and Spring


framework

This is more like a Hibernate and Spring tutorial. Takes you


through the key steps with code snippets. Also, very handy to
refresh your memory prior to your job interviews.

Q. What are the general steps involved in creating Hibernate


related class?
A. The general steps involved in creating Hibernate related classes
involve the following steps
 Define the domain (aka entity) objects like Employee,
Address, etc to represent relevant tables in the underlying
database with the appropriate annotations or using the
*.hbm.xml mapping files.
 Define the Repository (aka DAO -- Data Access Objects)
interfaces and implementations classes that use the domain
objects and the hibernate session to perform data base
CRUD (Create, Read, Update and Delete) operations the
hibernate way.
 Define the service interfaces and the classes that make use
of one or more repositories (aka DAOs) in a transactional
context.A transaction manager will be used to coordinate
transactions (i.e. commit or rollback) between a number of
repositories.
 Finally, use an IoC container like Spring framework to wire
up the Hibernate classes like SessionFactory, Session,
transaction manager, etc and the user defined repositories,
and the service classes. A number of interceptors can be
wired up as well for deadlock retry, logging, auditing, etc
using Spring.

For more detail, refer to Spring and Hibernate integration Interview


questions and answers
Q. How would you define a hibernate domain object with table mappings, native named
queries, and custom data conversion using annotations?
A.

Firstly, define a parent domain object class for any common method implementations.

?
1 package com.myapp.domain.model;
2
3 public class MyAppDomainObject {
4
//for example
5 protected boolean isPropertyEqual(Object comparee, Object
6 compareToo) {
7 if (comparee == null) {
8 if (compareToo != null) {
return false;
9 }
10 } else if (!comparee.equals(compareToo)) {
11 return false;
12 }
13 return true;
}
14
15 }
16
17

Now, extend the common DomainObject for specific DomainObject classes.

?
1 package com.myapp.domain.model;
2
3 @Entity
@org.hibernate.annotations.Entity(selectBeforeUpdate = true)
4 @Table(name = "tbl_employee")
5 @TypeDefs(value = { @TypeDef(name = "dec", typeClass =
6 DecimalUserType.class)}) // custom data type conversion
7
8 @NamedNativeQueries({
@NamedNativeQuery(name = "HighSalary", query = "select * from
9 tbl_employee where salary > :median_salary " , resultClass =
10 Employee.class),
11 @NamedNativeQuery(name = "LowSalary", query = "select * from
12 tbl_employee where salary < :median_salary " , resultClass =
13 Employee.class)
})
14
15 public class Employee extends MyAppDomainObject implements Serializable
16 {
17
18 @Id
19 @GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "employee_id")
20
private Long id;
21
22 @Column(name = "emp_code")
23 private String accountCode;
24
25 @Column(name = "manager_code")
26 private String adviserCode;
27
28
29
30 @Column(name = "type")
@Enumerated(EnumType.STRING)
31 private EmployeeType type = EmployeeType.PERMANENT;
32
33
34 @Type(type = "dec")
35 @Column(name = "base_salary")
36 private Decimal salary = Decimal.ZERO;
37
38 @Transient
private Decimal salaryWithBonus; //not persisted to database
39
40
@Formula("base_salary*2")
private Decimal doubleSalary; //derived or calculated read only
property
41
42 @Formula("(select base_salary where type = 'Permanent' )")
43 private Decimal permanantLeaveLoading; //derived or calculated
44 read only property
45
46
47 @OneToOne(cascade = { CascadeType.REFRESH })
@JoinColumn(name = "emp_code", insertable = false, updatable =
48
false)
49 private EmployeeExtrInfo extraInfo;
50
51 @ManyToOne(cascade = { CascadeType.REFRESH })
52 @JoinColumn(name = "manager_code", insertable = false, updatable =
53 false)
private Manager manager;
54
55 @OneToMany(cascade = { ALL, MERGE, PERSIST, REFRESH }, fetch =
56 FetchType.LAZY)
57 @JoinColumn(name = "emp_code", nullable = false)
58 @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE,
59 org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
private <span class="IL_AD" id="IL_AD6">List</span><PaymentDetail>
60 paymentDetails = new ArrayList<PaymentDetail>();
61
62 //getters and setters omitted for brevity

}
The dependency classes like EmployeeExtrInfo, Manager, and PaymentDetail will be
mapped in a similar manner as the Employee class. The EmployeeType enum class is
shown below. Also note the verys usefull annotations like @NamedNativeQueries,
@TypeDefs, and @Formula. The @Formula marks a property as derived, or calculated,
read-only property, where its value is calculated at fetch time using SQL expressions.

?
1 package com.myapp.domain.model;
2
3
4 public enum EmployeeType {
5
PERMANENT("Permanent"),
6 CONTRACTOR("Contractor"),
7 CASUAL("Casual");
8
9 private String type;
10
11 private EmployeeType (String type) {
this.type = type;
12
}
13
14
15
16 public String getType() {
return this.type;
17 }
18 }
19
The "dec" is a custom data type, you need to define the custom data type class. The
"salary" attribute will be making use of this special data type. This is ust a trivial
example, but more powerful custom type conversion classes can be created.

?
1 package com.myapp.domain.model;
2
3 import java.io.Serializable;
import java.math.BigDecimal;
4 import java.sql.PreparedStatement;
5 import java.sql.ResultSet;
6 import java.sql.SQLException;
7 import java.sql.Types;
import java.util.Properties;
8
9 import org.hibernate.usertype.ParameterizedType;
10 import org.hibernate.usertype.UserType;
11
12 public class DecimalUserType implements UserType, ParameterizedType {
13 public static final int PRECISION = 28;
14 public static final int SCALE = 15;
15
public int[] sqlTypes() {
16 return new int[]{Types.DECIMAL};
17 }
18
19 public Class<Decimal> returnedClass() {
20 return BigDecimal.class;
}
21
22 public boolean equals(Object x, Object y) {
23 if (x == y) {
24 return true;
25 }
26 if (x == null || y == null) {
return false;
27 }
28 return x.equals(y);
29 }
30
31 public int hashCode(Object x) {
32 return 0;
}
33
34 public Object nullSafeGet(ResultSet rs, String[] names, Object
35 owner) throws SQLException {
36 BigDecimal forReading = rs.getBigDecimal(names[0]);
37
38 if (forReading == null) {
return null;
39
}
40
41 return forReading.setScale(2, RoundingMode.HALF_EVEN);
42 //round to 2 decimal places
43 }
44
45
46 public void nullSafeSet(PreparedStatement st, Object value, int
index) throws SQLException {
47 if (value == null) {
48 st.setNull(index, Types.NUMERIC);
49 return;
50 }
51
52
53 BigDecimal forSaving = (BigDecimal) value;
st.setBigDecimal(index, forSaving.setScale(2,
54 RoundingMode.HALF_EVEN));
55 }
56
57 public Object deepCopy(Object value) {
58 return value;
59 }
60
public boolean isMutable() {
61 return false;
62 }
63
64 public Serializable disassemble(Object value) {
65 return null;
}
66
67 public Object assemble(Serializable cached, Object owner) {
68 return null;
69 }
70
71 public Object replace(Object original, Object target, Object
72 owner) {
return original;
73 }
74
75 public void setParameterValues(Properties parameters) {
76 }
77 }
78
79
80
81
82
83
84

The named queries are also shown above with the @NamedNativeQueries and
@NamedNativeQuery annotations. The parametrized values like :median_salary needs to
be supplied via the Hibernate repository class that makes use of the Employee domain
object. Firstly define the interface.

?
1 package com.myapp.domain.repo;
2
import java.util.List;
3
4
5 public interface EmployeeTableRepository {
6
7 Employee saveEmployee(Employee employee) throws RepositoryException ;
8 Employee loadEmployee(Long employeeId) throws RepositoryException ;
9 List<Employee> findAllEmployeesWithHighSalary(BigDecimal
10 medianSalary) throws RepositoryException;
List<Employee> findAllEmployeesWithLowSalary(BigDecimal medianSalary)
11 throws RepositoryException
12
13 }

Next the implementation of the above interface.


?
1 package com.myapp.domain.repo;
2
3
4 @SuppressWarnings("unchecked")
public class EmployeeTableHibernateRepository extends
5 HibernateDaoSupport implements EmployeeTableRepository {
6
7 public EmployeeTableHibernateRepository (HibernateTemplate
8 hibernateTemplate) {
9 setHibernateTemplate(hibernateTemplate);
}
10
11 //The employee objects gets constructed and passed to repo via the
12 Business Service layer
13 public Employee saveEmployee(Employee employee) throws
14 RepositoryException {
15 Session session =
getHibernateTemplate().getSessionFactory().getCurrentSession();
16 session.saveOrUpdate(employee);
17 session.flush();
18 session.evict(employee);
19 return this.loadEmployee(employee.getId());
}
20
21
public Employee loadEmployee(Long employeeId) throws
22 RepositoryException {
Session session =
getHibernateTemplate().getSessionFactory().getCurrentSession();
Criteria crit = session.createCriteria(Employee.class);
crit.add(Restrictions.eq("id",employeeId));
23 List<Employee> employees = crit.list();
24 if (employees.size() == 1) {
25 return employees.get(0);
}
26
27 //this is a custom exception class
28 throw new RepositoryException("Found more than one or no
29 employee with Id:" + employeeId);
30 }
31
32
33 public List<Employee> findAllEmployeesWithHighSalary(BigDecimal
medianSalary) throws RepositoryException {
34 Session session =
35 getHibernateTemplate().getSessionFactory().getCurrentSession();
36 Query query = session.getNamedQuery("HighSalary"); // query
37 name defined in Employee class
query.setBigDecimal(":median_salary", medianSalary); // query
38
parameter defined in Employee class
39 return (List<Employee>) query.list();
40 }
41
42 public List<Employee> findAllEmployeesWithLowSalary(BigDecimal
43 medianSalary) throws RepositoryException {
Session session =
44 getHibernateTemplate().getSessionFactory().getCurrentSession();
45 Query query = session.getNamedQuery("LowSalary"); // query
46 name defined in Employee class
47 query.setBigDecimal(":median_salary", medianSalary); // query
48 parameter defined in Employee class
return (List<Employee>) query.list();
49 }
50

//other methods can be defined here


}
The Service classes shown below will be making use of the repository (or DAO) classes.
The service class can use any number of the repository classes, and also responsible for
cordinating the transaction as well with a TransactionManger. In the example below, we
will be using the "PlatformTransactionManager" implementation provided by the
Spring framework.
?
1 package com.myapp.service;
2
3 public interface EmployeeService {
4
Employee saveEmployee(Employeee employee) throws
5 RepositoryException;
6 Employee loadEmployee(Long employeeId) throws RepositoryException;
7
8}
The implementation class is shown with the transaction manager. The
employeeRepository and transactionManager are dependency injected
?
1 package com.myapp.service;
2
3
4 import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
5 import org.springframework.transaction.TransactionStatus;
6 import
7 org.springframework.transaction.support.DefaultTransactionDefinition;
8 //....other imports
9
public class EmployeeServiceImpl implements EmployeeService {
10
11
12 private final EmployeeTableRepository employeeRepository;
13 private PlatformTransactionManager transactionManager;
14
15 public EmployeeServiceImpl (EmployeeTableRepository
16 employeeRepository, PlatformTransactionManager transactionManager) {
17 this.employeeRepository = employeeRepository;
this.transactionManager = transactionManager;
18 }
19
20
21 public Employee saveEmployee(Employeee employee) throws
22 RepositoryException {
23 TransactionStatus transactionStatus =
24 transactionManager.getTransaction(new
DefaultTransactionDefinition(
25 TransactionDefinition.PROPAGATION_REQUIRED));
26 try {
27 employee = this.employeeRepository.saveEmployee(employee);
28 } catch (Exception e) {
transactionManager.rollback(transactionStatus);
29 throw new RepositoryException(e);
30 } finally {
31 if (!transactionStatus.isCompleted()) {
32 transactionManager.commit(transactionStatus);
33 }
}
34 return employee;
35 }
36
37 public Employee loadEmployee(Long employeeId) throws
38 RepositoryException {
39 return this.employeeRepository.loadEmployee(employeeId);
}
40
41
42
43 //....other methods
44
45 }
46

Q. How will you wire up the code snippet discussed above using Spring?
A. The following 3 Spring configuration files are used for wiring up the classes defined
above.

 The daoContext.xml file to define the hibernate session factory, jndi data source,
hibernate properties, and the user defined domain class and the repository.
 The transactionContext.xml file to define the transaction manager.
 The servicesContext.xml to define the custom services class

Firstly the daoContext.xml file:

?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spr
2.5.xsd">

<bean id="dataSourceMyDB" class="org.springframework.jndi.JndiObjectFactoryBean"


scope="singleton">
<property name="jndiName">
<value>java:comp/env/jdbc/dataSource/mydb</value>
</property>
</bean>

<bean id="hibernateProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SybaseDialect</prop>
<prop key="hibernate.generate_statistics">false</prop>
<prop key="hibernate.hbm2ddl.auto">verify</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop
key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFact
</props>
</property>
<property name="location">
<value>classpath:/hibernate.properties</value>
</property>
</bean>

<bean id="hibernateAnnotatedClasses"
class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<value>com.myapp.domain.model.Employee</value>
</list>
</property>
</bean>

<bean id="hibernateSessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSourceShadow" />
<property name="hibernateProperties">
<ref local="hibernateProperties" />
</property>
<property name="entityInterceptor">
</property>
<property name="annotatedClasses">
<ref local="hibernateAnnotatedClasses" />
</property>
<property name="annotatedPackages">
<list></list>
</property>
</bean>

<bean id="daoTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">


<constructor-arg index="0" ref="sessionFactory" />
<constructor-arg index="1" value="true" />
</bean>

<!-- Repository beans -->


<bean id="employeeTableRepository"
class="com.myapp.domain.repo.EmployeeTableHibernateRepository">
<constructor-arg ref="daoTemplate" />
</bean>

</beans>

The transactionContext.xml
?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA)


-->

<alias name="hibernateSessionFactory" alias="sessionFactory"/>

<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="advisorAutoProxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
<bean id="transactionAttrSource"
class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor" ref="transactionInterceptor" />
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributeSource">
<bean
class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"
/>
</property>
</bean>

</beans>

Finally the servicesContext.xml

?
1 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
2
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:context="http://www.springframework.org/schema/context"
4 xmlns:aop="http://www.springframework.org/schema/aop"
5 xsi:schemaLocation="http://www.springframework.org/schema/beans
6 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
7 http://www.springframework.org/schema/context/spring-context-
8 2.5.xsd
9 http://www.springframework.org/schema/aop
10 http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
11
12 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
13 <!-- CONFIGURE SERVICE BEANS -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
14 <bean id="employeeService" class="com.myapp.service.EmployeeService">
15 <constructor-arg ref="employeeTableRepository" />
16 <constructor-arg ref="transactionManager" />
17 </bean>
18
19 </beans>
Q. How will you go about writing an integration or unit test for the EmployeeService
described above?
A. Since the dataSource is looked up via JNDI, you need to emulate the JNDI lookup.
This can be achieved with the Spring helper classes SimpleNamingContextBuilder and
DriverManagerDataSource.

This involves 3 steps.

 Define a bootsrapper class that emulates JNDI lookup using Spring helper classes
like SimpleNamingContextBuilder and DriverManagerDataSource. For example,
SybaseDevBootstrapper.java file.
 Wire-up this via a Spring config file named sybaseDevBootstrapContext.xml.
 Finally, write the JUnit test class EmployeeServicesSybTest.java.
 Define the TestExecutionListeners if required.

Firstly the SybaseDevBootstrapper.java

?
package com.myapp.test.db;

import javax.naming.NamingException;

import org.springframework.beans.factory.BeanCreationException;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;

/**
* helper class to bootstrap the sybase database datasources
*/
public class SybaseDevBootstrapper {

public static final String JNDI_BINDING_DB =


"java:comp/env/jdbc/dataSource/mydb";
public static final String DRIVER_CLASS =
"com.sybase.jdbc3.jdbc.SybDriver";

private SimpleNamingContextBuilder builder; //Spring JNDI emulator


class
/**
* setup sybase databases, and bind to specific places in jndi tree
*/
public void start() {
try {
builder =
SimpleNamingContextBuilder.emptyActivatedContextBuilder();

DriverManagerDataSource ds = new DriverManagerDataSource();


ds.setDriverClassName(DRIVER_CLASS);
ds.setUrl("jdbc:sybase:Tds:host-name:10004/my_db");
ds.setUsername("user");
ds.setPassword("pwd");
builder.bind(JNDI_BINDING_DB, ds);

} catch (NamingException e) {
throw new BeanCreationException(e.getExplanation());
}
}

public void stop() {


builder.deactivate();
builder.clear();
}
}

Next, wire the above Java class.

?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-
2.5.xsd"
default-autowire="byName">

<bean id="sybaseDevBootstrapper"
class="com.myapp.test.db.SybaseDevBootstrapper" init-method="start"
destroy-method="stop"/>

</beans>

Finally the test class EmployeeServicesSybTest.java


?
package com.myapp.test.services;

import javax.annotation.Resource;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import
org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

//...other imports

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:/sybaseDevBootstrapContext.xml",
"classpath:/transactionContext.xml",
"classpath:/daoContext.xml",
"classpath:/servicesContext.xml",
})
@TestExecutionListeners(value = {
DependencyInjectionTestExecutionListener.class,
SessionBindingHibernateListener.class
})

public class EmployeeServicesSybTest {

@Resource
EmployeeService employeeService;

@Test
public void testSaveEmployee() throws RepositoryException {
Assert.assertTrue(employeeService != null);

Employee employee = new Emloyee();


//....assign values here

employeeService.seaveEmployee(employee);

The JUnit's way of setting up cross cutting concerns like security, locale, currency,
timezone, and any other pre-initilization rules for the test cases function correctly is via
annoattions like @Before and @After. The Spring's TestContext framework uses the
anootation @TestExecutionListeners to acheive setting up of these cross cutting
concerns. In the above example, we are using the
DependencyInjectionTestExecutionListener.class from the Spring framework to provide
support for dependncy injection and the custom SessionBindingHibernateListener.class to
bind the session to the current thread. The custom implementation shown below extends
the AbstractTestExecutionListener, which is the abstract implementation of
TestExecutionListener class from the Spring framework.

?
/**
* Helper class for binding sessions to the current thread
*
*/
public class SessionBindingHibernateListener extends
SessionBindingListener {

private static final String BEAN_NAME = "hibernateSessionFactory";

public SessionBindingHibernateListener() {
super(BEAN_NAME);
}
}

?
1 import org.hibernate.FlushMode;
2 import org.hibernate.Session;
import org.hibernate.SessionFactory;
3 import org.springframework.context.ApplicationContext;
4 import org.springframework.orm.hibernate3.SessionFactoryUtils;
5 import org.springframework.orm.hibernate3.SessionHolder;
6 import org.springframework.test.context.TestContext;
import
7 org.springframework.test.context.support.AbstractTestExecutionListener;
8 import
9 org.springframework.transaction.support.TransactionSynchronizationManager;
10
11
12 /**
13 * Helper class for binding sessions to the current thread
*
14 */
15 public class SessionBindingListener extends AbstractTestExecutionListener {
16
17 private final String beanName;
18
19 public SessionBindingListener(String beanName) {
20 this.beanName = beanName;
}
21
22 @Override
23 public void prepareTestInstance(TestContext testContext) throws Exception
24 {
25 ApplicationContext context = testContext.getApplicationContext();
SessionFactory sessionFactory = (SessionFactory)
26 context.getBean(beanName);
27
28 Session session = SessionFactoryUtils.getSession(sessionFactory,
29 true);
30 session.setFlushMode(FlushMode.MANUAL);
31 if (!
TransactionSynchronizationManager.hasResource(sessionFactory)) {
32 TransactionSynchronizationManager.bindResource(sessionFactory,
33 new SessionHolder(session));
34 }
35 }
}

Spring and Hibernate Interview Questions and Answers: AOP,


interceptors, and deadlock retry

Before this, please refer to Hibernate


Interview Questions and Answers: with
annotations and Spring framework , as the
examples below are extension of this blog.

Q. How would you go about implementing a


dead lock retry service using Spring and
hibernate?
A.
 Define an annotation to annotate the
methods that needs deadlock retry
service. E.g. DeadlockRetry.java
 Define the interceptor that gets wired
up via AOP to perform the retry
functionality by invoking the
annotated method. E.g.
DeadlockRetryMethodInterceptor
 Wire up the annotation and deadlock
retry classes via Spring config. E.g.
transactionContext.xml
 Finally, annotate the method that
needs perform the retry service. E.g.
EmployeeServiceImpl -->
saveEmployee (....)

Define a custom annotation class DeadlockRetry.java.

?
1
2 package com.myapp.deadlock;
3
4 import java.lang.annotation.ElementType;
5 import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
6 import java.lang.annotation.RetentionPolicy;
7 import java.lang.annotation.Target;
8
9 @Retention(RetentionPolicy.RUNTIME)
10 @Target(ElementType.METHOD)
@Inherited
11 public @interface DeadlockRetry {
12
13 int maxTries() default 21;
14 int tryIntervalMillis() default 100;
15
16 }
17

Define the DeadlockRetryMethodInterceptor for performing retries. The above


annotation will be bound to the following implementation via Spring.

?
1 package com.myapp.deadlock;
2
3 import java.lang.reflect.Method;
4
import org.aopalliance.intercept.MethodInterceptor;
5 import org.aopalliance.intercept.MethodInvocation;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8 import org.hibernate.exception.LockAcquisitionException;
import org.springframework.dao.DeadlockLoserDataAccessException;
9
10
11 public class DeadlockRetryMethodInterceptor implements
12 MethodInterceptor {
13
14 private static final Logger LOGGER =
15 LoggerFactory.getLogger(DeadlockRetryMethodInterceptor.class);
16
17 @Override
public Object invoke(MethodInvocation invocation) throws Throwable
{
18 Object obj = invocation.getThis();
19 Method method = invocation.getMethod();
20 Method targetMethod =
obj.getClass().getMethod(method.getName(),
21 method.getParameterTypes());
22 DeadlockRetry dlRetryAnnotation =
23 targetMethod.getAnnotation(DeadlockRetry.class);
24 int maxTries = dlRetryAnnotation.maxTries();
25 int tryIntervalMillis = dlRetryAnnotation.tryIntervalMillis();
for (int i = 0; i < maxTries; i++) {
26 try {
27 LOGGER.info("Attempting to invoke " +
28 invocation.getMethod().getName());
29 Object result = invocation.proceed(); // retry
LOGGER.info("Completed invocation of " +
30
invocation.getMethod().getName());
31 return result;
32 } catch (Throwable e) {
33 Throwable cause = e;
34
35 //... put the logic to identify DeadlockLoserDataAccessException
or LockAcquisitionException
36 //...in the cause. If the execption is not due to deadlock,
37 throw an exception
38
39 if (tryIntervalMillis > 0) {
40 try {
41 Thread.sleep(tryIntervalMillis);
} catch (InterruptedException ie) {
42 LOGGER.warn("Deadlock retry thread interrupted", ie);
43 }
44 }
45
46 }
47
48 //gets here only when all attempts have failed
throw new RuntimeException
49 ("DeadlockRetryMethodInterceptor failed to successfully
50 execute target "
51 + " due to deadlock in all retry attempts",
52 new DeadlockLoserDataAccessException("Created by
53 DeadlockRetryMethodInterceptor", null));
}
}
Wire up the annotation and the interceptor via Spring config transactionContext.xml.
Only the snippet is shown.

?
1 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- Deadlock Retry AOP -->
2
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
3 <bean id="deadlockRetryPointcut"
4 class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
5
6 <constructor-arg><null/></constructor-arg>
7 <constructor-arg value="com.myapp.deadlock.DeadlockRetry" />
8 </bean>
9
10 <bean id="deadlockRetryMethodInterceptor"
class="com.myapp.deadlock.DeadlockRetryMethodInterceptor" />
11
12 <bean id="deadlockRetryPointcutAdvisor"
13 class="org.springframework.aop.support.DefaultPointcutAdvisor">
14 <constructor-arg ref="deadlockRetryPointcut" />
15 <constructor-arg ref="deadlockRetryMethodInterceptor" />
</bean>
16
17
Finally, annotate the method that needs to be retried in the event of dead lock issues.

?
1 package com.myapp.service;
2
3
4 import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
5 import org.springframework.transaction.TransactionStatus;
6 import
7 org.springframework.transaction.support.DefaultTransactionDefinition;
8 //....other imports
9
public class EmployeeServiceImpl implements EmployeeService {
10
11 private final EmployeeTableRepository employeeRepository;
12 private PlatformTransactionManager transactionManager;
13
14 public EmployeeServiceImpl(EmployeeTableRepository
15 employeeRepository, PlatformTransactionManager transactionManager) {
16 this.employeeRepository = employeeRepository;
this.transactionManager = transactionManager;
17 }
18
19
20 @DeadlockRetry
21 public Employee saveEmployee(Employeee employee) throws
22 RepositoryException {
23 TransactionStatus transactionStatus =
transactionManager.getTransaction(new
24 DefaultTransactionDefinition(
25 TransactionDefinition.PROPAGATION_REQUIRED));
26 try {
27 employee = this.employeeRepository.saveEmployee(employee);
} catch (Exception e) {
28 transactionManager.rollback(transactionStatus);
29 throw new RepositoryException(e);
30 } finally {
31 if (!transactionStatus.isCompleted()) {
32
33 transactionManager.commit(transactionStatus);
34 }
}
35 return employee;
36 }
37
38
39 //....other methods omitted for brevity
40
41 }
42

The above example gives a real life example using a custom annotation and AOP (i.e.
Aspect Oriented Programming).

Note: Also refer to Spring Interview Questions and Answers for explanation on
interceptors.

Q. What are the pros and cons between Spring AOP and AspectJ AOP?
A. The Spring AOP is simpler as it is achieved via a runtime dynamic proxy class. Unlike
AspectJ, it does not have to go through load time weaving or a compiler. The Spring AOP
uses the proxy and decorator design patterns.

Since Spring AOP uses proxy based AOP, it can only offer method execution pointcut,
whereas the AspectJ AOP supports all pointcuts.

In some cases, the compile time weaving (e.g. AspectJ AOP) offers better performance.

Hibernate Interview Questions and Answers: integration testing the DAO


layer

Before this, please refer to Hibernate Interview Questions and Answers: with annotations
and Spring framework , as the examples below are extension of this blog.

Q. How would you go about integration testing your DAO (i.e. Data Access Objects)
layer or your Hibernate Repository classes?
A. It is a bit tricky to write integration tests because any changes to the underlying data
can make your tests to fail. For example, addition of new records, modification to
existing data , etc. The key is to keep the data as static as possible. There are two possible
strategies.

1. Use a separate in memory database like HSQL (i.e. Hyper Structured Query
Language) Database. The data can be stored in flat text files -- say in pipe delimited
format and loaded into the in memory database during test set up phase, and deleted
during the test tear down phase.

2. The second alternative is to use a framework like DBUnit to extract the relevant data
from a given database and convert it into XML based data sets that can be inserted into
your test database during the test setUp phase and deleted in the test tear-down phase.
The DBUnit takes care of the data extraction and data load.

Both the above approaches maintain static data in either xml or text based flat files and
load the data during the test setup.

Q. How would you go about using an in memory database like HSQL DB?
A. It involves the following steps.

 Define the data in flat files. Say in pipe delimited format.


 Write the parser classes to read the data and convert them into SQL insert
statements to be loaded into the HSQL DB. This is known as ETL (i.e. Extract
Transform Load) operation.
 Override the default hibernate datasource proprties with the HSQL DB related
configuration.
 Wire up the datasource override file via override-daoContext.xml, which uses the
HSQL DB, and overrides the hibernate proprties from the file daoContext.xml.
 Write a mock HSQL DB JNDI bootstrap class and wire it up using a Spring
context file (e.g. hsqlBootstrapContext.xml).
 Write a database populator class that makes use of a number of parser classes to
populate the database. Also, wire this up via a Spring context file (e.g.
databasePopulatorContext.xml)
 Finally, the test class that bootstraps all the relevant classes via the Spring context
files using dependency injection.

Define the data in a flat file say employeeSearch.txt

?
1 #employee_id, emp_code, manager_code, type, base_salary
2 A342|XSMITH|A456|IM|Permanent|43,500.00
3 A342|YSMITH|A678|IM|Contract|57,700.00
Define the parser that loads the data by reading from the above flat file. For e.g.
EmployeeParser.java. This class can be further improved by moving out the methods
that will be shared by other parsers to a parent class or a helper class.

?
1 package com.myapp.database;
2
3 import java.io.BufferedReader;
import java.io.IOException;
4 import java.io.InputStreamReader;
5
6 import org.hibernate.HibernateException;
7 import org.hibernate.Query;
8 import org.hibernate.Session;
9
public class EmployeeParser {
10
11 private static final String DROP_SQL = "drop table tbl_employee";
12 private static final String CREATE_SQL =
13 "create table tbl_employee(employee_id varchar(12), emp_code
14 varchar(12), "+
15 "manager_code varchar(12), type varchar(12), base_salary
decimal";
16
17
18 private static final String INSERT_SQL =
19 "insert into tbl_employee (employee_id, emp_code, manager_code,
20 type, base_salary) values (";
21
22
23 public void parseEmployee(Session session) throws ParserException,
IOException {
24
createDatabaseTable(session);
25 BufferedReader file = findFile(getFileName());
26 String[] data = readLine(file);
27 while (data != null) {
28 Query query =
session.createSQLQuery(INSERT_SQL + "'" + data[0] + "','"
29 + data[1] + "','"
30 + data[2] + "'," + data[3] + "','" + data[4] +
31 ")");
32 query.executeUpdate();
33 data = readLine(file); // read next line from the file
}
34 }
35
36
37 protected String[] readLine(BufferedReader file) throws IOException {
38 String[] data = null;
39
40
41 String line = file.readLine();
42 while (line != null && line.startsWith("#")) {
43 line = file.readLine();
44 }
if (line != null) {
45
data = line.split("\\|"); //split by "|"
46 }
47 return data;
48 }
49
50
51 private void createDatabaseTable(Session session) {
Query query = session.createSQLQuery(DROP_SQL);
52 try {
53 query.executeUpdate();
54 } catch (HibernateException e) {
55 }
56 query = session.createSQLQuery(CREATE_SQL);
query.executeUpdate();
57 }
58
59 protected BufferedReader findFile(String fileName) {
60 final InputStreamReader file =
61 new
InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName));
62
BufferedReader stream = new BufferedReader(file);
63 return stream;
64 }
65
66
67
68 public String getFileName() {
69 return "employeeSearch.txt";
}
70
71 }
72
73
74

Define an override spring context file to override the actual datasource properies. For
example, the actual database could be Sybase or Oracle. The override-daoContext.xml
is shown below.

?
1 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
2
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:context="http://www.springframework.org/schema/context"
4
5 xsi:schemaLocation="http://www.springframework.org/schema/beans
6 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
7 http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
8 default-autowire="byName">
9
10 <bean id="hibernateProperties"
11 class="org.springframework.beans.factory.config.PropertiesFactoryBean">
12 <property name="properties">
13 <props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop
14 <prop key="hibernate.generate_statistics">true</prop>
15 <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
16 <prop key="hibernate.jdbc.batch_size">1000</prop>
17 <prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
18
<prop key="hibernate.cache.use_query_cache">false</prop>
19 <prop key="hibernate.cache.use_second_level_cache">false</prop>
20 <prop
21 key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorF
22 </props>
</property>
23 <property name="location">
24 <value>classpath:/hibernate.properties</value>
25 </property>
26 </bean>
27
28
29 </beans>

Write a JNDI based bootstrapper class HsqlDevBootstrapper.java that emulates JNDI


boot strapping for your test classes. This is achieved via the Spring mock class
SimpleNamingContextBuilder.

?
1 package com.myapp.test.db;
2
3 import javax.naming.NamingException;
4
import org.springframework.beans.factory.BeanCreationException;
5 import org.springframework.jdbc.datasource.DriverManagerDataSource;
6 import org.springframework.mock.jndi.SimpleNamingContextBuilder;
7
8 /**
9 * helper class to bootstrap the sybase database datasources
10 */
public class HsqlDevBootstrapper {
11
12 public static final String JNDI_BINDING_DB =
13 "java:comp/env/jdbc/dataSource/mydb";
14 public static final String DRIVER_CLASS = "org.hsqldb.jdbcDriver";
15
16
17 private SimpleNamingContextBuilder builder; //Spring JNDI mock class
18
19 /**
20 **/setup HSQL DB, and bind it to jndi tree
21 public void start() {
22 try {
23 builder =
24 SimpleNamingContextBuilder.emptyActivatedContextBuilder();
25
DriverManagerDataSource ds = new
26 DriverManagerDataSource();
27 ds.setDriverClassName(DRIVER_CLASS);
28 ds.setUrl("jdbc:hsqldb:mem:my_db"); //in memory HSQL DB
29 URL
30 ds.setUsername("user");
ds.setPassword("pwd");
31 builder.bind(JNDI_BINDING_DB, ds);
32
33
34 } catch (NamingException e) {
35 throw new BeanCreationException(e.getExplanation());
36 }
37 }
38
public void stop() {
39 builder.deactivate();
40 builder.clear();
41 }
42
43 }
44

Wire up the JNDI bootstrap class via Spring the config file hsqlBootstrapContext.xml.

?
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:context="http://www.springframework.org/schema/context"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
6 http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-
7 2.5.xsd"
8 default-autowire="byName">
9
10 <bean id="hsqlBootstrapper"
11 class="com.myapp.test.db.HsqlBootstrapper" init-method="start" destroy-
method="stop"/>
</beans>

Define a DatabasePopulator.java class that populates all the relevant (i.e. associated)
database tables incling tbl_employee.

?
1 package com.myapp.database;
2
3 import java.io.BufferedReader;
import java.io.IOException;
4 import java.io.InputStreamReader;
5 import java.sql.SQLException;
6 import java.text.ParseException;
7 import java.util.HashMap;
import java.util.List;
8 import java.util.Map;
9
10 import org.hibernate.HibernateException;
11 import org.hibernate.Query;
12 import org.hibernate.Session;
13 import org.hibernate.Transaction;
import org.springframework.orm.hibernate3.HibernateTemplate;
14
15
16 public class DatabasePopulator {
17
18 private final HibernateTemplate daoTemplate;
19
20
21
22 public DatabasePopulator(HibernateTemplate daoTemplate) throws
23 Exception {
this.daoTemplate = daoTemplate;
24
try {
25 createDB();
26 } catch (Exception e) {
27 throw e;
28 }
}
29
30 /**
31 * This is where all the loading happens
32 */
33 public void createDB() throws HibernateException, SQLException,
34 IOException {
Session session =
35 daoTemplate.getSessionFactory().openSession();
36
37 Transaction tran = session.beginTransaction();
38
39 //make use of the parser to read the data from a file and load it
40 (i.e. ETL operation - Extract, Transaform, and Load)
41
42 EmployeeParser empParser = new EmployeeParser();
43 empParser.parseEmployee(session);
44
45 //load other relevant data
46
47 session.flush();
48 tran.commit();
}
49
50 }
51
52

Wire the DatabasePopulator via Spring config file databasePopulatorContext.xml.

?
<?xml version="1.0" encoding="UTF-8"?>
1 <beans xmlns="http://www.springframework.org/schema/beans"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
4 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
5 http://www.springframework.org/schema/context
6 http://www.springframework.org/schema/context/spring-context-
7 2.5.xsd"
default-autowire="byName">
8
9 <bean name="databasePopulator"
10 class="com.myapp.database.DatabasePopulator">
11 <constructor-arg ref="daoTemplate" />
12 </bean>
13
</beans>

Finally the test class. Some of the missing Spring context files and classes were defined
in a different blog entry mentioned at the beginning of this blog. The context files need to
be loaded in the right order.

?
package com.myapp.repository;

import static org.junit.Assert.assertEquals;


import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;

import org.joda.time.DateTime;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import
org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:hsqlBootstrapContext.xml",
"classpath:transactionContext.xml",
"classpath:daoContext.xml",
"classpath:override-daoContext.xml",
"classpath:databasePopulatorContext.xml"
})
@TestExecutionListeners(value = {
DependencyInjectionTestExecutionListener.class,
SessionBindingHibernateListener.class
})

public class EmployeeRepositoryTest {

@Resource
EmployeeTableRepository tableRepository;

@Test
public void testLoadEmployee() {

Assert.assertTrue(tableRepository != null);
Emplyee employee = null;
try {
employee = tableRepository.loadEmployee("A342");
} catch (RepositoryException e) {
fail("Load employee threw an exception " + e);
}

assertTrue(employee != null);
assertEquals(employee.getType(), "Permanent");
//...more assertions

//....test other repository methods like saveEmployee, etc.


}
Understanding Hibernate proxy objects and avoiding potential pitfalls

Q. How does hibernate support lazy loading?


A. Hibernate uses a proxy object to support lazy loading. Basically as soon as you
reference a child or lookup object via the accessor/getter methods, if the linked entity is
not in the session cache (i.e. the first-level cache), then the proxy code will go off to the
database and load the linked object. It uses javassist (or CGLIB ) to effectively and
dynamically generate sub-classed implementations of your objects.

Let's look at an example. An employee hierarchy table can be represented in a database


table as shown below

?
public class Employee {

private Long id;


private String name;
private String title;
private Employee superior;
private Set<Employee> subordinates;

//getters and setters are omitted

}
In the above example, if you use lazy loading then the "superior" and "subordinates" will
be proxied (i.e. not the actual object, but the stub object that knows how to load the actual
object) when the main "Employee" object is loaded. So, if you need to get the
"subordinates" or "superior" object, you invoke the getter method on the employee like
employee.getSuperior( ) and the actual object will be loaded.

Q. What are some of the pitfalls of using a proxy object?


A. The typical pitfall is in how you implement your equals( ) method that gets invoked
when comparing objects.

Pitfall 1: As explained before, the proxy objects are dynamically created by sub-
classing your object at runtime. The subclass will have all the methods of the parent, but
the fields (e.g. name, etc) in the proxy object will remain null, and when any of the
methods are accessed via getter/setter method, the proxy loads up the real object from the
database.

A typical equals( ) method implementation will look like

?
1
2 @Override
3 public boolean equals(Object obj) {
4 if (obj == null) {
return false;
5 }
6 if (obj == this) {
7 return true;
8 }
if (!(obj instanceof Employee)) { //Line X: compare object
9
type
10 return false;
11 }
12 return name.equals((Employee)obj).name); //Line Y: compare names
13 }
14
As discussed before, the supplied object is a proxy object and the supplied name will be
null. This can be fixed by using the getter method in Line Y instead of using the field
directly. Using the getter method will tell the proxy object to load the actual object as
shown below.

?
1
2 @Override
3 public boolean equals(Object obj) {
4 if (obj == null) {
return false;
5 }
6 if (obj == this) {
7 return true;
8 }
if (!(obj instanceof Employee)) {
9
return false;
10 }
11 return name.equals((Employee)obj).getName()); //Line Y: compare
12 names
13 }
14
Pitfall 2: We saw earlier that the the proxy objects are dynamically created by sub-
classing your object. In a simple scenario where you only have the "Employee" object
the typecasting in Line Y and "instanceof" operator in Line X will work. But, what will
happen if you have a type hierarchy for The class Employee as shown below

?
1 public class PermanentEmployee extends Employee {
.......
2
}
3
4 public class CasualEmployee extends Employee {
5 .......
6}
7
8

When you have a type hierarchy as shown above, the type casts and instanceof operators
will not work with the proxy objects.

A proxy class is a subclass of a field type that is required. Hibernate creartes a dynamic
subclass by looking at the type of field. This means that if the field type is not the actual
implementation (e.g. CasualEmployee) type, but an interface or superclass (e.g.
Employee), the type of the proxy will be different than the type of the actual object. So,
as shown in the diagram below, If the field type is the superclass (e.g. Employee) of the
actual implementation (i.e. CasualEmployee), the proxy type (i.e. proxy) and the
implementation-type (e.g. CasualEmployee) will be siblings in the type hierarchy, both
extending the superclass. This means the proxy object will not be an instance of the
implementing type (i.e. CasualEmployee), and the application code depending on this
check will fail.

To prevent this issue, you have two approaches.

Approach 1: Switch off proxying on the top level class by setting lazy=”false”, which
will turn proxying off for the hierachy.

Approach 2: Use the "Gang of Four" (i.e. GoF) visitor design pattern that allows you to
adapt a single object or manipulate a collection of polymorphic objects without all the
messy typecasts and instanceof operations.

Pitfall 3: As per the above example, if you have an “Employee” class, that contains a
“name” property, when you invoke do “employee.getName()”, the proxies will get the
"name" from Hibernate caches (either 1st or 2nd levels) or the database when requested.
But if this call happens in the presentation layer like in the Struts action class, you will
get the org.hibernate.LazyInitializationException because the Hibernate Session is
closed and this lazy attribute does not have the session attached, hence can’t load
their lazy references.
The solution is to de-proxy the employee class as shown below:

Step 1: Write a generic utility class to de-proxy a given object

?
1 public class HibernateUtil {
2
3 public static <T> T unproxy(T entity) {
4 if (entity == null) {
5 return null;
6 }
7
if (entity instanceof HibernateProxy) {
8 Hibernate.initialize(entity);
9 entity = (T) ((HibernateProxy)
10 entity).getHibernateLazyInitializer().getImplementation();
11 }
12
13 return entity;
}
14 }
15

Step 2: Use the above utility class

?
1 public Employee getSuperior() {
2 superior = HibernateUtils.unproxy(employee);
3 return superior;
}
4

These types of issues are hard to debug, and being aware of these pitfalls can save you
lots of time in debugging and fixing the issues.

You might also like