Chapter 8. Persistence Add-On

The persistence add-on provides a convenient way to create Java Persistence API (JPA v2) compliant entities. There are different commands available to configure JPA, create new JPA-compliant entities, and add fields to these entities. In the following a summary of the features offered by the Spring Roo persistence add-on:

8.1. JPA setup command

The jpa setup command provides the following options and attributes:

Database Options:

* The JDBC driver dependencies for these databases are not available in public Maven repositories. As such, Roo configures a default dependency in your project pom.xml. You need to adjust it according to your specific version of your database driver available in your private Maven repository.

Some useful hints to get started with Oracle Express (Oracle XE): After installing Oracle XE you need to find the JDBC driver under ${oracle-xe}/app/oracle/product/10.2.0/server/jdbc/lib and run the command:

mvn install:install-file -Dfile=ojdbc14_g.jar -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.2 -Dpackaging=jar -DgeneratePom=true 

Also, if you dont want Jetty (or Tomcat) to be conflicting with oracle-xe web-server, you should use the following command: mvn jetty:run -Djetty.port=8090.

ORM Provider Options:

In addition, the jpa setup command accepts optional databaseName, userName and password attributes for your convenience. However, it's not necessary to use this command. You can easily edit these details in the database.properties file at any time. Finally, you can also specify a pre-configured JNDI datasource via the jndiDataSource attribute.

The jpa setup command can be re-run at any time. This means you can change your ORM provider or database when you plan to move your application between your development setup (e.g. Hibernate with HSQLDB) to your production setup (e.g. EclipseLink with DB2). Of course this is a convenience only. You'll naturally experience fewer deployment issues if you use the same platform for both development and production.

Running the jpa setup command in the Roo shell takes care of configuring several aspects in your project:

  1. JPA dependencies are registered in the project pom.xml Maven configuration. It includes the JPA API, ORM provider (and its dependencies), DB driver, Spring ORM, Spring JDBC, Commons DBCP, and Commons Pool

  2. Persistence XML configuration with a persistence-unit preconfigured based on your choice of ORM provider and Database. Here is an example for the EclipseLink ORM provider and HSQL database:

    <persistence xmlns="http://java.sun.com/xml/ns/persistence" 
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" 
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
                                     http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">    
    
        <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
            <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
            <properties>
                <property name="eclipselink.target-database" 
                          value="org.eclipse.persistence.platform.database.HSQLPlatform"/>
    
                <!--value='drop-and-create-tables' to build a new database on each run; 
                    value='create-tables' creates new tables if needed; 
                    value='none' makes no changes to the database-->
                <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
    
                <property name="eclipselink.ddl-generation.output-mode" value="database"/>
    
                <property name="eclipselink.weaving" value="static"/>
            </properties>
        </persistence-unit>
    </persistence>
    

    By default the persistence unit is configured to build a new database on each application restart. This helps to avoid data inconsistencies during application development when the domain model is not yet finalized (new fields added to an entity will yield new table columns). If you feel that your domain model is stable you can manually switch to a mode which allows data persistence across application restarts in the persistence.xml file. This is documented in the comment above the relevant property. Each ORM provider uses different property names and values to achieve this.

  3. A database properties file (src/main/resources/META-INF/spring/database.properties) which contains user name, password, JDBC driver name and connection URL details:

    database.url=jdbc\:hsqldb\:mem\:foo
    database.username=sa
    database.password=
    database.driverClassName=org.hsqldb.jdbcDriver
    

    This file can be edited manually, or you can use the properties set command, or by using the databaseName, userName and password attributes of the jpa setup command. You can edit the properties file or use any of these commands at any time.

  4. A DataSource definition and a transaction manager are added to the Spring application context:

    [...]
    <bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
        <property name="driverClassName" value="${database.driverClassName}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
    </bean>
    
    <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
        
    <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
          id="entityManagerFactory">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    

8.2. Entity JPA command

Using the entity jpa command you can create simple Java beans which are annotated with JPA annotations. There are several optional attributes which can be used as part of this command but in its simplest form it will generate the following artifacts:

roo> entity jpa --class ~.Person   
Created SRC_MAIN_JAVA/com/foo
Created SRC_MAIN_JAVA/com/foo/Person.java
Created SRC_MAIN_JAVA/com/foo/Person_Roo_JavaBean.aj
Created SRC_MAIN_JAVA/com/foo/Person_Roo_Jpa_Entity.aj
Created SRC_MAIN_JAVA/com/foo/Person_Roo_Jpa_ActiveRecord.aj
Created SRC_MAIN_JAVA/com/foo/Person_Roo_ToString.aj
Created SRC_MAIN_JAVA/com/foo/Person_Roo_Configurable.aj
~.Person roo>

As you can see from the Roo shell messages there are 6 files generated (also, note that the context has changed to the Person type in the Roo shell):

  1. Person.java:

    @RooJavaBean
    @RooToString
    @RooJpaActiveRecord
    public class Person {
    }
    

    You will notice that by default, the Person type does not contain any fields (these will be added with the field commands or manually in the type) or methods.

  2. Person_Roo_JavaBean.aj (this will only be generated when fields are added to the Person type)

    The first annotation added by the entity jpa command is the @RooJavaBean annotation. This annotation will automatically add public accessors and mutators via an ITD for each field added to the Person type. This annotation (like all Roo annotations) has source retention (so it will not be present in the generated byte code).

  3. Person_Roo_ToString.aj

    The second annotation added to the Person type is the @RooToString annotation. This annotation will generate a toString method for the Person type via an ITD. The toString() method will contain a concatenated representation of all field names and their values using the commons-lang RefectionToStringBuilder by default. If you want to provide your own toString() method alongside the Roo generated toString() method you can declare the toStringMethod attribute in the @RooToString annotation. This attribute allows you to change the default method name of the Roo-managed toString() (default name) method, thereby allowing your custom toString() method alongside the Roo-managed method.

  4. Person_Roo_Configurable.aj

    This ITD is automatically created and does not require the @RooConfigurable annotation to be introduced into the Person.java type. It takes care of marking the Person type with Spring's @Configurable annotation. This annotation allows you to inject any types from the Spring bean factory into the Person type. The injection of the JPA entity manager (which is defined as a bean in the application context) is possible due to the presence of the @Configurable annotation.

  5. Person_Roo_Jpa_Entity.aj

    The forth annotation is the @RooJpaActiveRecord annotation. This annotation triggers the creation of two ITDs: the Person_Roo_Jpa_Entity.aj ITD and the Person_Roo_Jpa_ActiveRecord.aj ITD. Note that If you do not want ActiveRecord-style methods in your domain object you can just use the @RooJpaEntity annotation.

    The JPA @Entity annotation is added to the Person_Roo_Jpa_Entity.aj ITD. This annotation marks the Person as persistable. By default, the JPA implementation of your choice will create a table definition in your database for this type. Once fields are added to the Person type, they will be added as columns to the Person table.

    privileged aspect Person_Roo_Jpa_Entity {
        
        declare @type: Person: @Entity;   
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "id")
        private Long Person.id;
        
        @Version
        @Column(name = "version")
        private Integer Person.version;
        
        public Long Person.getId() {
            return this.id;
        }
        
        public void Person.setId(Long id) {
            this.id = id;
        }
        
        public Integer Person.getVersion() {
            return this.version;
        }
        
        public void Person.setVersion(Integer version) {
            this.version = version;
        }
    }
    

    As can be seen, the Person_Roo_Jpa_Entity.aj ITD introduces two fields by default. An id field (which is auto-incremented) and a version field (used for JPA-managed optimistic locking).

  6. Person_Roo_Jpa_ActiveRecord.aj

    As mentioned previously, the @RooJpaActiveRecord annotation also triggers the creation of the Person_Roo_Jpa_ActiveRecord.aj ITD. This contains a number of persistence related CRUD methods into your Person type via the ITD:

    privileged aspect Person_Roo_Jpa_ActiveRecord {
        
        @PersistenceContext
        transient EntityManager Person.entityManager;
        
        @Transactional
        public void Person.persist() {
            if (this.entityManager == null) this.entityManager = entityManager();
            this.entityManager.persist(this);
        }
        
        @Transactional
        public void Person.remove() {
            if (this.entityManager == null) this.entityManager = entityManager();
            if (this.entityManager.contains(this)) {
                this.entityManager.remove(this);
            } else {
                Person attached = this.entityManager.find(this.getClass(), this.id);
                this.entityManager.remove(attached);
            }
        }
        
        @Transactional
        public void Person.flush() {
            if (this.entityManager == null) this.entityManager = entityManager();
            this.entityManager.flush();
        }
        
        @Transactional
        public Person Person.merge() {
            if (this.entityManager == null) this.entityManager = entityManager();
            Person merged = this.entityManager.merge(this);
            this.entityManager.flush();
            return merged;
        }
    
        
        public static final EntityManager Person.entityManager() {
            EntityManager em = new Person().entityManager;
            if (em == null) throw new IllegalStateException("Entity manager has not been \
                            injected (is the Spring Aspects JAR configured as an AJC/AJDT \
                            aspects library?)");
            return em;
        }
        
        public static long Person.countPeople() {
            return entityManager().createQuery("select count(o) from Person o", Long.class)
                                            .getSingleResult();
        }
        
        @SuppressWarnings("unchecked")
        public static List<Person> Person.findAllPeople() {
            return entityManager().createQuery("select o from Person o", Person.class).getResultList();
        }
        
        public static Person Person.findPerson(Long id) {
            if (id == null) return null;
            return entityManager().find(Person.class, id);
        }
        
        @SuppressWarnings("unchecked")
        public static List<Person> Person.findPersonEntries(int firstResult, int maxResults) {
            return entityManager().createQuery("select o from Person o", Person.class)
                       .setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
        }
    }
    

    The Person_Roo_Jpa_ActiveRecord.aj ITD introduces a number of methods such as persist(), remove(), merge(), flush() which allow the execution of ActiveRecord-style persistence operations on each Roo-managed JPA entity. Furthermore, a number of persistence-related convenience methods are provided. These methods are countPeople(), findAllPeople(), findPerson(..), and findPersonEntries(..).

    All persistence methods are configured with Spring's Transaction support (Propagation.REQUIRED, Isolation.DEFAULT).

    Similar to the @RooToString annotation you can change the default method name for all persistence-related methods generated through the @RooJpaActiveRecord annotation. For example:

    @RooJpaActiveRecord(persistMethod = "save")

The entity jpa command offers a number of optional (but very useful) attributes worth mentioning. For example the --testAutomatically attribute can be used to have Roo to generate and maintain integration tests for the Person type (and the persistence methods generated as part of it). Furthermore, the --abstract and --extends attributes allow you to mark classes as abstract or inheritance patterns. Of course this can also be done directly in the Java sources of the Person type but sometimes it is useful to do this through a Roo command which can be scripted and replayed if desired. Other attributes allow you to define the identifier field name as well as the identifier field type which, in turn, allows the use of complex identifier types.

8.3. Field commands

As mentioned earlier in this chapter the field commands allow you to add pre-configured field definitions to your target entity type (Person.java in our example). In addition to simply adding the field names and types as defined via the command the appropriate JPA annotations are added to the field definitions. For example adding a birth day field to the Person.java type with the following command ...

~.Person roo> field date --fieldName birthDay --type java.util.Date
Managed SRC_MAIN_JAVA/com/foo/Person.java
Created SRC_MAIN_JAVA/com/foo/Person_Roo_JavaBean.aj
Managed SRC_MAIN_JAVA/com/foo/Person_Roo_ToString.aj
~.Person roo>

... yields the following field definition in Person.java:

@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(style = "M-")
private Date birthDay;

You'll notice that the @Temporal annotation is part of the JPA specification and defines how date values are persisted to and retrieved from the database in a transparent fashion. The @DateTimeFormat annotation is part of the Spring framework and takes care of printing and parsing Dates to and from String values when necessary (especially Web frontends frequently take advantage of this formatting capability).

Also note that Roo created a Person_Roo_JavaBean.aj ITD to generate accessors and mutators for the birthDay field and it also updated the toString() method to take the birthDay field into account.

Aside from the Date (and Calendar) type, the field command offers String, Boolean, Enum, Number, Reference and Set types. The Reference and Set types are of special interest here since they allow you to define relationships between your entities:

  1. The field reference command will create a JPA many-to-one (default) or one-to-one relationship:

    ~.Person roo> field reference --fieldName car --type com.foo.Car
    

    The field definition added to the Person type contains the appropriate JPA annotations:

    @ManyToOne
    @JoinColumn
    private Car car;
    

    The optional --cardinality command attribute allows you to define a one-to-one relationship (via JPAs @OneToOne annotation) between Person and Car if you wish:

    @OneToOne
    @JoinColumn
    private Car car;

    You can add the mappedBy attribute to the @OneToOne annotation to define the FK name handled by the inverse side (Car) of this relationship.

    Consider the following constraint: when you delete a Person, any Car they have should also be deleted, but not vice versa (i.e. you should be able to delete a Car without deleting its owner). In the database, the foreign key should be in the "car" table.

    @Entity
    @RooJavaBean
    @RooJpaActiveRecord
    public class Person {
    
        // Inverse side ("car" table has the FK column)
        @OneToOne(cascade = CascadeType.ALL, mappedBy = "owner")
        private Car car;
    
    }
    @Entity
    @RooJavaBean
    @RooJpaActiveRecord
    public class Car {
    
        // Owning side (this table has the FK column)
        @OneToOne
        @JoinColumn
        private Person owner;
    }

    If you delete a Person from the Person list, both the Person and the Car are deleted. So the cascading works. But if you delete a Car, the transaction will roll back and you will see an exception due it being referenced by a person. To overcome this situation you can add the following method to your Car.java:

    @PreRemove
    private void preRemove() {
        this.getOwner().setCar(null);
    }

    This hooks into the JPA lifecycle callback function and will set the reference between Person and Car to null before attempting to remove the record.

  2. The field set command will allow you to create a many-to-many (default) or a one-to-many relationship:

    field set --fieldName cars --type com.foo.Car

    The field definition added to the Person type contains the appropriate JPA annotation:

    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Car> cars = new HashSet<Car>();

    To change the mapping type to one-to-many simply use the --cardinality attribute. To achieve a true m:n relationship you will need to issue the field set commands for both sides of the relationship.

Like the entity jpa command, the field command offeres a number of optional (but very useful) attributes worth mentioning. For example, you can change the field / column name translations with the --column attribute. Furthermore there are a number of attributes which translate directly to their equivalents defined in JSR 303 (Bean Validation). These attributes include --notNull, --sizeMin, --sizeMax and other related attributes. Please refer to the field command in the appendix to review the different attributes offered.