Monday 3 September 2012

Spring Web JPA Hibernate In-Memory Example

This post introduces the web version of the Standalone Hibernate JPA In-Memory example. It relies on a very simple implementation of the Spring MVC framework. This example is available from Github in the Spring-Web-JPA-Roundtrip directory.

Configuration

Instead of using the traditional JPA persistence.xml configuration file, we use Java configuration. On top of the traditional WebConfig class, we have a JpaConfig class annotated with @EnableTransactionManagement to enable database transactions.

The LocalContainerEntityManagerFactoryBean is the easiest mean to configure JPA. Notice that the persistence.xml database connection information is moved to the DataSource. The PlatformTransactionManager is required to process @Transactional annotated method (discussed further).

PersistenceExceptionTranslationPostProcessor converts any vendor specific exceptions into Spring DataAccessException for easier handling.
@Configuration
@EnableTransactionManagement
public class JpaConfig {

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){

        LocalContainerEntityManagerFactoryBean lcemfb
            = new LocalContainerEntityManagerFactoryBean();

        lcemfb.setDataSource(this.dataSource());
        lcemfb.setPackagesToScan(new String[] {"com.jverstry"} );
        lcemfb.setPersistenceUnitName("MyPU");

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

        Properties ps = new Properties();
        ps.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
        ps.put("hibernate.hbm2ddl.auto", "create");
        lcemfb.setJpaProperties(ps);

        lcemfb.afterPropertiesSet();

        return lcemfb;

    }

    @Bean
    public DataSource dataSource(){

        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("org.hsqldb.jdbcDriver");
        ds.setUrl("jdbc:hsqldb:mem:testdb");
        ds.setUsername("sa");
        ds.setPassword("");

        return ds;

    }

    @Bean
    public PlatformTransactionManager transactionManager() {

        JpaTransactionManager tm = new JpaTransactionManager();

        tm.setEntityManagerFactory(
            this.entityManagerFactoryBean().getObject() );

        return tm;

    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

}

Data Item, Controller & Repository

The persisted data relies on Pojomatic:
@Entity
@AutoProperty
public class Item implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long ID;

    private String name = "";

    // Setter, Getters, Pojomatic methods...


}
The controller calls the injected repository class for a CRUD roundtrip, and retrieves the generated messages in the model:
@Controller
public class MyController {

    @Autowired
    private MyRepository rep;

    @RequestMapping(value = "/")
    public String home(Model model) {
        return "index";
    }

    @RequestMapping(value = "/roundtrip")
    public String Roundtrip(Model model) {

        model.addAttribute("Messages", rep.performRoundtrip());

        return "roundtrip";

    }

}
The repository annotated class, performs a CRUD (Create, Read, Update, Delete):
@Repository
public class MyRepository {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public List<String> performRoundtrip() {

        List<String> l = new ArrayList<String>();

        try {

            l.add("Before create");
            create(l);

            l.add("Before read I");
            read(l);

            l.add("Before update");
            update(l);

            l.add("Before read II");
            read(l);

            l.add("Before delete");
            delete(l);

            l.add("Before read III");
            read(l);

        } catch (Exception ex) {

            l.add(ex.toString());

        }

        return l;

    }

    private Item i = null;

    @Transactional
    public void create(List<String> l) {
        i = new Item();
        i.setName("Item A");
        l.add("- Before saving   : " + i);
        em.persist(i);
        l.add("- After saving    : " + i);
    }

    @Transactional
    public void read(List<String> l) {
        Item retr = em.find(Item.class, this.i.getID());
        l.add("- Retrieved       : " + retr);
    }

    @Transactional
    public void update(List<String> l) {
        i.setName("Item B");
        l.add("- Updated         : " + i);
        em.persist(i);
    }

    @Transactional
    public void delete(List<String> l) {
        l.add("- Deleting        : " + i);
        em.remove(i);
    }

}

JSP Page

The following roundtrip.jsp displays the messages collected during the roundtrip:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Roundtrip !!!</title>
    </head>
    <body>
        <c:forEach items="${Messages}" var="element">
            ${element}<br />
        </c:forEach><br />
        <a href="<c:url value='/'/>" />Home</a>
    </body>
</html>

Running the example

Once compiled, the example can be run with mvn tomcat:run. Then, browse http://localhost:8585/spring-web-jpa-roundtrip/.

The generated output is:
Before create
 - Before saving : Item{ID: {0}, name: {Item A}}
 - After saving : Item{ID: {1}, name: {Item A}}
Before read I
 - Retrieved : Item{ID: {1}, name: {Item A}}
Before update
 - Updated : Item{ID: {1}, name: {Item B}}
Before read II
 - Retrieved : Item{ID: {1}, name: {Item B}}
Before delete
 - Deleting : Item{ID: {1}, name: {Item B}}
Before read III
 - Retrieved : null

More Spring related posts here.

No comments:

Post a Comment