...
Starting with Tapestry 5.3, Tapestry provides built-in integration with the Java Persistence API (JPA) through the Tapestry-jpa module. This module supersedes Tynamo's JPA integration.
...
Downloading
The Tapestry-jpa module is not automatically included in Tapestry applications because of the additional dependencies it requires. If you're using Maven, just add the tapestry-jpa
dependency to your application's pom.xml file, something like this:
Code Block |
---|
language | xml |
---|
title | pom.xml (partial) |
---|
|
<dependency>
<groupId>org.apache.tapestry</groupId>
<artifactId>tapestry-jpa</artifactId>
<version>${tapestry-version}</version>
</dependency> |
If you aren't using Maven, you'll have to download the jar and its dependencies yourself.
Selecting a JPA Implementation
The Tapestry-jpa module includes a dependency on a JPA specification (API) from Geronimo but not an implementation. You'll have to chose a JPA implementation, such as EclipseLink or Hibernate. The Tapestry-jpa module assumes you'll use Eclipselink. You just have to add the EclipseLink dependency:
Code Block |
---|
language | xml |
---|
title | pom.xml (partial) for EclipseLink |
---|
|
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>${eclipselink-version}</version>
</dependency> |
Or, if you'd rather use Hibernate as your JPA implementation, you'll want to exclude either the Gernonimo or Hibernate JPA specification JAR:
Code Block |
---|
language | xml |
---|
title | pom.xml (partial) for Hibernate |
---|
|
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate-version}</version>
<exclusions>
<exclusion>
<!-- omit Geronimo JPA spec to avoid conflict with Hibernate JPA spec -->
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jpa_2.0_spec</artifactId>
</exclusion>
</exclusions>
</dependency> |
Configuring JPA
The persistence.xml file is the standard configuration file in JPA used to define the persistence units. Tapestry reads this file to create the EntityManagerFactory. The following example demonstrates a persistence.xml file.
...
Code Block |
---|
language | xml |
---|
lang | xml |
---|
title | persistence.xml |
---|
|
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="2.0">
<persistence-unit name="JTAUnit" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>
jdbc/JPATest
</non-jta-data-source>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
<property name="eclipselink.logging.level" value="fine"/>
</properties>
</persistence-unit>
</persistence> |
Now let's see how to provide the same configuration without XML. The following demonstrates an equivalent JPA configuration.
Code Block |
---|
language | java |
---|
title | AppModule.java (partial) |
---|
|
public class AppModule {
@Contribute(EntityManagerSource.class)
public static void configurePersistenceUnitInfos(MappedConfiguration<String,PersistenceUnitConfigurer> cfg) {
PersistenceUnitConfigurer configurer = new PersistenceUnitConfigurer() {
public void configure(TapestryPersistenceUnitInfo unitInfo) {
unitInfo.nonJtaDataSource("jdbc/JPATest")
.addProperty("eclipselink.ddl-generation", "create-tables")
.addProperty("eclipselink.logging.level", "fine");
}
};
cfg.add("JTAUnit", configurer);
}
} |
...
If you have additional packages containing entities, you may contribute them to the JpaEntityPackageManager service configuration.
Code Block |
---|
language | java |
---|
title | AppModule.java (partial) |
---|
|
public class AppModule {
@Contribute(JpaEntityPackageManager.class)
public static void providePackages(Configuration<String> configuration) {
configuration.add("org.example.myapp.domain");
configuration.add("com.acme.model");
}
} |
...
Depending on whether more than one persistence unit has been defined, the way to inject EntityManager varies slightly. Let’s start with a simple scenario, where only a single persistence unit is defined. In this case, an EntityManager can be injected using the @Inject annotation.
Code Block |
---|
language | java |
---|
title | CreateAddress.java |
---|
|
public class CreateAddress {
@Inject
private EntityManager entityManager;
@Property
private Address address;
@CommitAfter
void onSuccess() {
entityManager.persist(address);
}
} |
Alternatively, you can use the @PersistenceContext annotation to get the EntityManager injected into a page or component, as shown in the following example.
Code Block |
---|
language | java |
---|
title | CreateAddress.java |
---|
|
public class CreateAddress {
@PersistenceContext
private EntityManager entityManager;
@Property
private Address address;
@CommitAfter
void onSuccess() {
entityManager.persist(address);
}
} |
If you have multiple instances of persistence-unit defined in the same application, you need to explicitly tell Tapestry which persistence unit you want to get injected. This is what the @PersistenceContext annotation’s name attribute is used for? The following example demonstrates how to inject the persistence unit named DemoUnit.
Code Block |
---|
language | java |
---|
title | CreateAddress.java |
---|
|
public class CreateAddress {
@PersistenceContext(unitName = "DemoUnit")
private EntityManager entityManager;
@Property
private Address address;
@CommitAfter
@PersistenceContext(unitName = "DemoUnit")
void onSuccess() {
entityManager.persist(address);
}
} |
...
While component injection occurs only on fields, the injection in the IoC layer may be triggered by a field or a constructor. The following example demonstrates field injection, when a single persistence unit is defined in the persistence descriptor.
Code Block |
---|
language | java |
---|
title | UserDaoImpl.java |
---|
|
public class UserDAOImplUserDaoImpl implements UserDAOUserDao {
@Inject
private EntityManager entityManager;
...
} |
The constructor injection is demonstrated in the following example.
Code Block |
---|
language | java |
---|
title | UserDaoImpl |
---|
|
public class UserDAOImplUserDaoImpl implements UserDAOUserDao {
private EntityManager entityManager;
public UserDAOImplUserDaoImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
...
} |
If multiple persistence units are defined in the same application, you need to disambiguate the unit to inject. This is done with the @PersistenceContext annotation, as shown in the following example. Because @PersistenceContext must not be placed on constructor parameters, you can’t use constructor injection and must switch to field injection.
Code Block |
---|
language | java |
---|
title | UserDaoImpl |
---|
|
public class UserDAOImplUserDaoImpl implements UserDAOUserDao {
@Inject
@PersistenceContext(unitName = "DemoUnit")
private EntityManager entityManager;
...
} |
...
As you may already know from the Hibernate integration library, Tapestry automatically manages transactions for you. The JPA integration library defines the @CommitAfter annotation, which acts as the correspondent annotation from the Hibernate integration library. Let’s explore the UserDAO UserDao interface to see the annotation in action.
Code Block |
---|
language | java |
---|
title | UserDao.java |
---|
|
public interface UserDAOUserDao {
@CommitAfter
@PersistenceContext(unitName = "DemoUnit")
void add(User user);
List<User> findAll();
@CommitAfter
@PersistenceContext(unitName = "DemoUnit")
void delete(User... users);
} |
...
After placing the @CommitAfter annotation on methods, you need to tell Tapestry to advise those methods. This is accomplished by adding the transaction advice, as shown in the following example.
Code Block |
---|
language | java |
---|
title | AppModule.java (partial) |
---|
|
public class AppModule {
@Match("*DAODao")
public static void adviseTransactionally(
JpaTransactionAdvisor advisor,
MethodAdviceReceiver receiver) {
advisor.addTransactionCommitAdvice(receiver);
}
} |