Hibernate Interview Questions and Answers: with annotations and Spring framework
Spring Interview Questions and Answers Q1 - Q14 are FAQs
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
|
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.
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: Hibernate
4 Comments:
hi.
this useful to my carrier,and very to understand about java...
Good to understand the steps and annotations in a quick still comprehensive way..
Very nicely explained
Well explained...
Post a Comment
Subscribe to Post Comments [Atom]
<< Home