Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

Your Next Java Web App: Less XML, No Long Restarts, Fewer Hassles (Part 2)

Note: This tutorial continues from Part 1 and is gonna show you how to code Java without any of the traditional B.S., like XML and app server restarts. It’s split into 3 parts so that your brain doesn’t freak out at a 50 meter long webpage. Enjoy!

Part 1 | Part 2 | Part 3

DOWNLOAD THE PDF


——————————

Introducing Hibernate

Adding Hibernate to the mix is rather easy. All I need to do is add a few beans to the WebappConfig class. Which beans to add depends whether you wish to stick to the classic Hibernate API that uses SessionFactory or switch to a modern JPA, which uses EntityManager instead. Note that JPA is a specification and we’re still using Hibernate as an implementation. The basic concepts and programming model are the same for classic Hibernate and JPA, but in my opinion JPA is easier to set up and more beautiful, not to mention it being an official JEE specification. By the way, even when using plain old Hibernate, the class annotations such as @Entity still belong to JPA.

 

There are a couple of things to do in order to introduce Hibernate. First, I don’t want to hardcode database connection parameters and would prefer them to be in the db.properties file. This can be achieved by annotating the class with @PropertySource("classpath:db.properties") and introducing a field:

@Autowired Environment env;

Secondly, I need to create a dataSource bean:

@Bean(destroyMethod = "close")
public DataSource getDataSource() {
 DriverManagerDataSource ds = new DriverManagerDataSource(env.getProperty("url"));
 ds.setDriverClassName(env.getProperty("driver"));
 ds.setUsername(env.getProperty("user"));
 ds.setPassword(env.getProperty("pass"));

  return ds;
}

For development time, using DriverManagerDataSource is just fine, but for production I would recommend a proper connection pool such as commons-dbcp. In that case, the method body would look like this:

@Bean
public DataSource getDataSource() { 
 BasicDataSource ds = new BasicDataSource();
 ds.setUrl(env.getProperty("url"));
 ds.setDriverClassName(env.getProperty("driver"));
 ds.setUsername(env.getProperty("user"));
 ds.setPassword(env.getProperty("pass"));
 ds.setValidationQueryTimeout(5);
 ds.setValidationQuery("select 1 from DUAL");

 return ds;
}

I’m using org.apache.commons.dbcp.BasicDataSource here and also added a keepalive query to prevent closing the connection.

How to add commons-dbcp to the pom:

<!-- DB connection pooling for production applications -->
<dependency>
 <groupId>commons-dbcp</groupId>
 <artifactId>commons-dbcp</artifactId>
 <version>1.4</version>
</dependency>

The properties are stored in src/main/resources/db.properties:

driver=org.hsqldb.jdbcDriver
url=jdbc:hsqldb:res:snoutdb
hibernate.dialect=org.hibernate.dialect.HSQLDialect
user=SA
pass=

Note that I’m using HSQLDB as a database here. It’s great for testing and it’s easy to include the necessary test data to the application. I’m using snoutdb as a database name (more on that later) and it’s accompanied by two additional files in src/main/resources: snoutdb.properties which is HSQL’s configuration and snoutdb.script that contains my application’s schema and sample data.

There are three more things to add: EntityManager factory, TransactionManager and an interceptor that allows session/entityManager to be used in a view layer. However, they depend on whether I’m using JPA or pure Hibernate.

DOWNLOAD THE PDF


JPA

For JPA , adding the entityManager factory is easy:

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 
 LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
 emf.setDataSource(getDataSource());
 emf.setPackagesToScan("com.zt.helloWeb.model");

 //let Hibernate know which database we're using.
 //note that this is vendor specific, not JPA
 Map<String, Object> opts = emf.getJpaPropertyMap();
 opts.put("hibernate.dialect", env.getProperty("hibernate.dialect"));

 HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter(); 
 emf.setJpaVendorAdapter(va);

 return emf;
}

And transactionManager. This is important because it makes it possible to avoid starting and flushing transactions/sessions manually. It works in concert with @EnableTransactionManagement on the config class and @Transactional on the DAO class.

@Bean
public PlatformTransactionManager transactionManager() { 
 return new JpaTransactionManager(entityManagerFactory().getObject());
}

In order to add the interceptor, override the addInterceptors method of WebMvcConfigurerAdapter. Without it, you cannot use lazy loading because the session would be unavailable in the view script. You would have to eagerly load all the data in the view script.

@Override
public void addInterceptors(InterceptorRegistry registry) {
 OpenEntityManagerInViewInterceptor interceptor = new OpenEntityManagerInViewInterceptor();
 interceptor.setEntityManagerFactory(entityManagerFactory().getObject());
 registry.addWebRequestInterceptor(interceptor);
}

Pure Hibernate

For pure Hibernate, the steps are the same, but details differ.

Hibernate uses SessionFactory instead of EntityManagerFactory:

@Bean
public SessionFactory buildSessionFactory() {
 LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(getDataSource());
 return builder.scanPackages("com.zt.helloWeb.model").buildSessionFactory();
}

TransactionManager is also different:

@Bean
public HibernateTransactionManager transactionManager() {
 return new HibernateTransactionManager(sessionFactory());
}

And the interceptor class differs too. Note that there are separate versions of OpenSessionInViewInterceptor classes depending on whether Hibernate 3 or 4 is used.

@Override
public void addInterceptors(InterceptorRegistry registry) {
 OpenSessionInViewInterceptor interceptor = new OpenSessionInViewInterceptor();
 interceptor.setSessionFactory(buildSessionFactory());
 registry.addWebRequestInterceptor(interceptor);
}

Hibernate also needs to be added to pom.xml. Again, this is different whether or not JPA is used:

<!-- When using JPA -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>4.1.2</version>
</dependency>
<!-- When using pure Hibernate -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>4.1.2</version>
</dependency>

There is one more difference between setting up JPA and Hibernate. For JPA, the EntityManager is injected (not EntityManagerFactory), and it’s done via @PersistenceContext annotation:

@PersistenceContext
private EntityManager entityManager;

Hibernate’s SessionFactory is injected the standard way:

@Autowired
private SessionFactory sessionFactory;

The complete WebappConfig class I end up with is this:

@Configuration
@ComponentScan("com.zt.helloWeb")
@EnableWebMvc
@PropertySource("classpath:db.properties")
@EnableTransactionManagement
public class WebappConfigJpa extends WebMvcConfigurerAdapter {

  @Autowired
  Environment env;  

  //Tell SpingMVC where to find view scripts
  @Bean
  public InternalResourceViewResolver setupViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");

    return resolver;
  }

  //Enable serving static resources even when DispatcherServlet is mapped to /  
  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }

  //Enable accessing entityManager from view scripts. Required when using lazy loading 
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    OpenEntityManagerInViewInterceptor interceptor = new OpenEntityManagerInViewInterceptor();
    interceptor.setEntityManagerFactory(entityManagerFactory().getObject());
    registry.addWebRequestInterceptor(interceptor);
  }

  //Set up dataSource to be used by Hibernate. Also make sure the connection doesn't go down
  @Bean
  public DataSource getDataSource() {    
    BasicDataSource ds = new BasicDataSource();
    ds.setUrl(env.getProperty("url"));
    ds.setDriverClassName(env.getProperty("driver"));
    ds.setUsername(env.getProperty("user"));
    ds.setPassword(env.getProperty("pass"));
    ds.setValidationQuery("select 1 from DUAL");
    return ds;
  }

  //Set up JPA and transactionManager
  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setDataSource(getDataSource());
    emf.setPackagesToScan("com.zt.helloWeb.model");

    //let Hibernate know which database we're using.
    //note that this is vendor specific, not JPA
    Map opts = emf.getJpaPropertyMap();
    opts.put("hibernate.dialect", env.getProperty("hibernate.dialect"));

    HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();    
    emf.setJpaVendorAdapter(va);

    return emf;

  }

  @Bean
  public PlatformTransactionManager transactionManager() {    
    return new JpaTransactionManager(entityManagerFactory().getObject());
  }
}

Continue to Part 3

DOWNLOAD THE PDF



  • doug1234567

    Very detail and helpful tutorial you have here. I haven’t been actively developing webapp for almost a year since I moved to work mostly for a project providing services with no front-end/web technology. Now I think I miss the web app development.

  • Nice tutorial

  • Lucas Murata

    Best tutorial

  • S.M.

    Very nice tutorial, however I must be missing one part since all my requests return a 404. Where is setupViewResolver called from?