Google

Feb 1, 2012

Hibernate Interview Questions and Answers: with annotations and Spring framework

Spring Interview Questions and Answers Q1 - Q14 are FAQs

Q1 - Q4 Overview & DIP Q5 - Q8 DI & IoC Q9 - Q10 Bean Scopes Q11 Packages Q12 Principle OCP Q14 AOP and interceptors
Q15 - Q16 Hibernate & Transaction Manager Q17 - Q20 Hibernate & JNDI Q21 - Q22 read properties Q23 - Q24 JMS & JNDI Q25 JDBC Q26 Spring MVC Q27 - Spring MVC Resolvers

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.

Q17. What are the general steps involved in creating Hibernate related class?
A17. 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
Q18. How would you define a hibernate domain object with table mappings, native named queries, and custom data conversion using annotations?
A18.

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


package com.myapp.domain.model;

public class MyAppDomainObject {
    
 //for example
    protected boolean isPropertyEqual(Object comparee, Object compareToo) {
        if (comparee == null) {
            if (compareToo != null) {
                return false;
            }
        } else if (!comparee.equals(compareToo)) {
            return false;
        }
        return true;
    }

}


Now, extend the common DomainObject for specific DomainObject classes.


package com.myapp.domain.model;

@Entity
@org.hibernate.annotations.Entity(selectBeforeUpdate = true)
@Table(name = "tbl_employee")
@TypeDefs(value = { @TypeDef(name = "dec", typeClass = DecimalUserType.class)})  // custom data type conversion

@NamedNativeQueries({
    @NamedNativeQuery(name = "HighSalary", query = "select * from tbl_employee where salary > :median_salary " , resultClass = Employee.class),
 @NamedNativeQuery(name = "LowSalary", query = "select * from tbl_employee where salary < :median_salary " ,  resultClass = Employee.class)
})

public class Employee extends MyAppDomainObject implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "employee_id")
    private Long id;
 
    @Column(name = "emp_code")
    private String accountCode;

    @Column(name = "manager_code")
    private String adviserCode;

 
 
    @Column(name = "type")
    @Enumerated(EnumType.STRING)
    private EmployeeType type = EmployeeType.PERMANENT;
 

    @Type(type = "dec")
    @Column(name = "base_salary")
    private Decimal salary = Decimal.ZERO;
 
    @Transient
    private Decimal salaryWithBonus; //not persisted to database
 
    @Formula("base_salary*2")
    private Decimal doubleSalary;   //derived or calculated read only property
 
    @Formula("(select base_salary where type = 'Permanent' )")
    private Decimal permanantLeaveLoading;   //derived or calculated read only property
 
 
    @OneToOne(cascade = { CascadeType.REFRESH })
    @JoinColumn(name = "emp_code", insertable = false, updatable = false)
    private EmployeeExtrInfo extraInfo;

    @ManyToOne(cascade = { CascadeType.REFRESH })
    @JoinColumn(name = "manager_code", insertable = false, updatable = false)
    private Manager manager;

    @OneToMany(cascade = { ALL, MERGE, PERSIST, REFRESH }, fetch = FetchType.LAZY)
    @JoinColumn(name = "emp_code", nullable = false)
    @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
    private List<PaymentDetail> paymentDetails = new ArrayList<PaymentDetail>();

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

package com.myapp.domain.model;


public enum EmployeeType {
    
    PERMANENT("Permanent"),
    CONTRACTOR("Contractor"),
    CASUAL("Casual");
    
    private String type;
    
    private EmployeeType (String type) {
        this.type = type;
    }
    
    public String getType() {
        return this.type;
    }
}


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.

package com.myapp.domain.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

public class DecimalUserType implements UserType, ParameterizedType {
    public static final int PRECISION = 28;
    public static final int SCALE = 15;

    public int[] sqlTypes() {
        return new int[]{Types.DECIMAL};
    }

    public Class<Decimal> returnedClass() {
        return BigDecimal.class;
    }

    public boolean equals(Object x, Object y) {
        if (x == y) {
            return true;
        }
        if (x == null || y == null) {
            return false;
        }
        return x.equals(y);
    }

    public int hashCode(Object x) {
        return 0;
    }

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException {
        BigDecimal forReading = rs.getBigDecimal(names[0]);

        if (forReading == null) {
            return null;
        }

        return forReading.setScale(2, RoundingMode.HALF_EVEN);   //round to 2 decimal places
    }


    public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException {
        if (value == null) {
            st.setNull(index, Types.NUMERIC);
            return;
        }
  
  
        BigDecimal forSaving = (BigDecimal) value;
        st.setBigDecimal(index, forSaving.setScale(2, RoundingMode.HALF_EVEN));
    }

    public Object deepCopy(Object value) {
        return value;
    }

    public boolean isMutable() {
        return false;
    }

    public Serializable disassemble(Object value) {
        return null;
    }

    public Object assemble(Serializable cached, Object owner) {
        return null;
    }

    public Object replace(Object original, Object target, Object owner) {
        return original;
    }

    public void setParameterValues(Properties parameters) {
    }
}



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.

package com.myapp.domain.repo;

import java.util.List;


public interface EmployeeTableRepository {

 Employee saveEmployee(Employee employee) throws RepositoryException ; 
 Employee loadEmployee(Long employeeId) throws RepositoryException ;
 List<Employee> findAllEmployeesWithHighSalary(BigDecimal medianSalary) throws RepositoryException;
 List<Employee> findAllEmployeesWithLowSalary(BigDecimal medianSalary) throws RepositoryException 

}



Next the implementation of the above interface.

package com.myapp.domain.repo;


@SuppressWarnings("unchecked")
public class EmployeeTableHibernateRepository extends HibernateDaoSupport implements EmployeeTableRepository {

    public EmployeeTableHibernateRepository (HibernateTemplate hibernateTemplate) {
        setHibernateTemplate(hibernateTemplate);
    }

 //The employee objects gets constructed and passed to repo via the Business Service layer
    public Employee saveEmployee(Employee employee) throws RepositoryException {
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        session.saveOrUpdate(employee);
        session.flush();
        session.evict(employee);
        return this.loadEmployee(employee.getId());
    }
 
 public Employee loadEmployee(Long employeeId) throws RepositoryException {
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        Criteria crit = session.createCriteria(Employee.class);
        crit.add(Restrictions.eq("id",employeeId));
        List<Employee> employees = crit.list();
        if (employees.size() == 1) {
            return employees.get(0);
        }
  
  //this is a custom exception class
        throw new RepositoryException("Found more than one or no employee with Id:" + employeeId);
    }
 
 
 public List<Employee> findAllEmployeesWithHighSalary(BigDecimal medianSalary) throws RepositoryException {
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query query = session.getNamedQuery("HighSalary");    // query name defined in Employee class
        query.setBigDecimal(":median_salary", medianSalary);  // query parameter defined in Employee class 
        return (List<Employee>) query.list();
    }
 
 public List<Employee> findAllEmployeesWithLowSalary(BigDecimal medianSalary) throws RepositoryException {
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query query = session.getNamedQuery("LowSalary");     // query name defined in Employee class
        query.setBigDecimal(":median_salary", medianSalary);  // query parameter defined in Employee class 
        return (List<Employee>) query.list();
    }

    
  //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.
package com.myapp.service;

public interface EmployeeService {

   Employee saveEmployee(Employeee employee) throws RepositoryException;
   Employee loadEmployee(Long employeeId) throws RepositoryException;

}

The implementation class is shown with the transaction manager. The employeeRepository and transactionManager are dependency injected
package com.myapp.service;


import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
//....other imports

public class EmployeeServiceImpl implements EmployeeService {

    
    private final EmployeeTableRepository employeeRepository;
 private PlatformTransactionManager transactionManager;
   
    public EmployeeServiceImpl (EmployeeTableRepository employeeRepository, PlatformTransactionManager transactionManager) {
        this.employeeRepository = employeeRepository;
  this.transactionManager = transactionManager;
    }


    public Employee saveEmployee(Employeee employee) throws RepositoryException {
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(new DefaultTransactionDefinition(
                        TransactionDefinition.PROPAGATION_REQUIRED));
        try {
            employee = this.employeeRepository.saveEmployee(employee);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new RepositoryException(e);
        } finally {
            if (!transactionStatus.isCompleted()) {
                transactionManager.commit(transactionStatus);
            }
        }
        return employee;
    }
 
 public Employee loadEmployee(Long employeeId) throws RepositoryException {
        return this.employeeRepository.loadEmployee(employeeId);
    }
 
 
 //....other methods

}




Q19. How will you wire up the code snippet discussed above using Spring?
A19. 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/spring-tx-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.ClassicQueryTranslatorFactory</prop>
   </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

<?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:aop="http://www.springframework.org/schema/aop"
 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/aop 
   http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <!-- CONFIGURE SERVICE BEANS         -->
 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <bean id="employeeService" class="com.myapp.service.EmployeeService">
  <constructor-arg ref="employeeTableRepository" />
  <constructor-arg ref="transactionManager" />
 </bean>
 
</beans>

Q20. How will you go about writing an integration or unit test for the EmployeeService described above?
A20. 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);
 }
}






import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.transaction.support.TransactionSynchronizationManager;


/**
 * Helper class for binding sessions to the current thread
 * 
 */
public class SessionBindingListener extends AbstractTestExecutionListener {
    
    private final String beanName;
    
 public SessionBindingListener(String beanName) {
  this.beanName = beanName;
 }
    
 @Override
 public void prepareTestInstance(TestContext testContext) throws Exception {
  ApplicationContext context = testContext.getApplicationContext();
  SessionFactory sessionFactory = (SessionFactory) context.getBean(beanName);
  
        Session session = SessionFactoryUtils.getSession(sessionFactory, true);
        session.setFlushMode(FlushMode.MANUAL);
        if (!TransactionSynchronizationManager.hasResource(sessionFactory)) {
            TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
        }
 }
}


Labels:

4 Comments:

Anonymous Interview Questions said...

hi.
this useful to my carrier,and very to understand about java...

4:32 PM, February 10, 2012  
Blogger Unknown said...

Good to understand the steps and annotations in a quick still comprehensive way..

5:12 AM, November 29, 2012  
Anonymous Anonymous said...

Very nicely explained

5:42 AM, October 21, 2013  
Anonymous Anonymous said...

Well explained...

8:03 PM, July 25, 2014  

Post a Comment

Subscribe to Post Comments [Atom]

<< Home