Saturday, October 18, 2014

Spring JPA/Hibernate configuration without XML

With the lovely spring feature @Configuration you can now completely drop all XML configuration. In this blog I will go through how to configure JPA/Hibernate and get rid of XML bean configuration and the persistence.xml often used with JPA. Start by instrumenting the configuration class with:

  @Configuration   
  @EnableJpaRepositories(basePackages = "com.blogspot.jpdevelopment.immutable.hibernate")   
  @EnableTransactionManagement   
  public class JpaConfig {   


@Configuration Indicates that a class declares one or more @Bean methods and may be processed by the Spring container.

@EnableJpaRepositories Will scan the package of the annotated configuration class for Spring Data repositories. This means classes annotated with @Repository.

@EnableTransactionManagement Enables Spring's annotation-driven transaction management capability, similar to the support found in Spring's <tx:*> XML namespace. Typically the XML configuration looks like this:

 <tx:annotation-driven transaction-manager="transactionManager"/>  

Now it's time to declare beans. A javax.sql.DataSource bean is needed.
 @Bean  
 public DataSource dataSource() {  
      BasicDataSource dataSource = new BasicDataSource();  
      dataSource.setDriverClassName("com.mysql.jdbc.Driver");  
      dataSource.setUrl("jdbc:mysql://localhost:3306/test");  
      dataSource.setUsername("root");  
      return dataSource;  
 }  

Next we need a JpaVendorAdapter. This serves as single configuration point for all vendor-specific properties.

 @Bean  
 public JpaVendorAdapter jpaVendorAdapter() {  
      HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();  
      hibernateJpaVendorAdapter.setShowSql(false);  
      hibernateJpaVendorAdapter.setGenerateDdl(true);  
      hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);  
   
      return hibernateJpaVendorAdapter;  
 }  

With the datasource and jpaVendorAdaptor in place we can make a LocalContainerEntityManagerFactoryBean. This will expose a EntityManagerFactory and inject it in the classes defined in the packagesToScan.
 @Bean  
 public LocalContainerEntityManagerFactoryBean entityManagerFactory() {  
      LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();  
      entityManagerFactoryBean.setDataSource(dataSource());  
      entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());  
      entityManagerFactoryBean.setPackagesToScan("com.blogspot.jpdevelopment.immutable.hibernate");  
      return entityManagerFactoryBean;  
 }   

Finally we need a PlatformTransactionManager.

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


Now lets the it for a spin and make a repository. A simple way to get started is to use the CrudRepository interface.

 @Repository  
 public interface PersonRepository extends CrudRepository<Person, UUID> {  
 }  

And the implementation.

 public class PersonAccessRepository implements PersonRepository {  
   
      @PersistenceContext  
      private EntityManager entityManager;  
   
      @Override  
      public Person findOne(UUID id) {  
           return this.entityManager.find(Person.class, id);  
      }  
   
      @Transactional  
      @Override  
      public <S extends Person> S save(S person) {  
           this.entityManager.persist(person);  
           return person;  
      }  
 }  


Full example with dependencies and executable code can be found here.

Working with immutable objects and hibernate

To support a rich domain model, it's often a good idea to make domain objects immutable. Hibernate if however not happy about this. It works best with objects who only has a default constructor and getter and setters for all fields. 
To make hibernate work with immutable objects, declare a default constructor with at least package level visibility. 
If mapping-annotations is placed on fields the access type will be AccessType.FIELD. If placed on methods the access type will be AccessType.PROPERTY. Field type access is needed by hibernate for handling final attributes.
 
 @Entity  
 @Table(name = "person")  
 public class Person {  
   
      @Id  
      @Column(columnDefinition = "BINARY(16)", length = 16)  
      private final UUID id;  
      private final Date creationDate;  
      private final String firstname;  
      private final String lastname;  
   
      // Hibernate needs this or it will fail with an InstantiationException  
      private Person() {  
           this.id = UUID.randomUUID();  
           this.creationDate = new Date();  
           this.firstname = null;  
           this.lastname = null;  
      }  
   
      public Person(String firstname, String lastname) {  
           this.id = UUID.randomUUID();  
           this.creationDate = new Date();  
           this.firstname = firstname;  
           this.lastname = lastname;  
      }  
 }  

If you are using XML mappings, this can also be done by setting:

<hibernate-mapping default-access="field" package="com.blogspot.jpdevelopment.immutable.hibernate.core.domain">

A full example is available here.