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.