Chapter 6. Castor JDO - Support for the JPA specification

6.1. JPA annotations - Motivation

It has always been a goal of the Castor JDO project to eventually fully support the JPA specification and become a first class JPA provider that can e.g. be easily integrated with Spring ORM. Whilst full compliance is still work in progress, there are several small areas where sufficient progress has been made, and where partial support will be made available to the user community.

One such area is (partial) support for JPA annotations. This chapter highlights how JPA-annotated Java classes can be used with Castor JDO to persist such classes through the existing persistence framework part of Castor, without little additional requirements.

The following sections describe ...

  1. The prerequisites.

  2. The current limitations.

  3. The supported JPA annotations.

  4. How to use Castor JDO to persist JPA-annotated classes.

  5. How to use Castor JDO as Spring ORM provider to persist JPA-annotated classes.

6.2. Prerequisites and outline

The following sections assume that you have a (set of) JPA-annotated domain classes which you would like to persist using Castor JDO.

As such, we explain how to enlist those classes with Castor JDO (through the JDOClassDescriptorResolver interface, so that Castor JDO will be able to find and work with your JPA-annotated classes. In addition, we explain how to achieve the same with Spring ORM and the Spring ORM provider for Castor JDO.

By the end of this chapter is should become obvious that Castor JDO is well-prepared to integrate with the annotation part of the JPA specification, although support for JPA annotations is currently limited.

6.3. Limitations and Basic Information

6.3.1. persistence.xml

In Castor JPA there is no use or support for a JPA persistence.xml configuration file for now. All required configuration needs to be supplied by one of the following means:

  • Castor JDO configuration file.

  • JDOClassDescriptorResolver configuration.

  • Spring configuration file for the Spring ORM provider for Castor JDO.

6.3.2. JPA access type and the placing of JPA annotations

Because Castor does not support direct field access, this feature is not supported by Castor JPA. Thus all annotations have to be defined on the getter methods of the fields. If JPA related annotations are found on fields, Castor will throw an exception.

6.3.3. Primary Keys

Primary keys made of single fields are supported by Castor as defined in the JPA specification (through the use of the @Id annotation). If you need to define composite primary keys, please note that that Castor does not support relations with composite primary keys.

If you still want to persist single classes with the use of composite primary keys, none of the available JPA annotations (@EmbeddedId or @IdClass) is supported as such. Instead Castor uses a kind of ad-hoc IdClass mechanism. Simply define multiple @Id annotations on the fields that make up your composite primary key, and Castor JDO will internally create the relevant constructs.

6.3.4. Inheritance, mapped superclasses, etc.

These JPA annotations are currently not supported by Castor JDO. For now, you can only define entities.

6.3.5. Relations

Besides the fact, that Castor does not support composite primary keys in relations, there are some limitations on the different kinds of relations between entities. For detailed information, please read the documentation about the different relations types further below.

6.4. An outline of JPA-Annotations

S ... Supported
PS ... Partially Supported
NS ... Not Supported

Table 6.1. JPA-Annotations

AnnotationSupportedComment
AssociationOverride NS  
AssociationOverrides NS  
AttributeOverride NS  
AttributeOverrides NS  
Basic S See information on Castor fetch types!
Column PS Supported: column name, nullable
ColumnResult NS  
DiscriminatorColumn NS Castor does not support Joined Table Class Hierachy.
DiscriminatorValue NS Castor does not support Joined Table Class Hierachy.
Embeddable NS  
Embedded NS  
EmbeddedId NS Castor does not support composed primary keys embedded in classes of their own.
Entity S This annotation is needed to tell Castor that this Class is an entity.
EntityListeners S  
EntityResult NS  
Enumerated S  
ExcludeDefaultListeners S  
ExcludeSuperclassListeners S  
FieldResult NS  
GeneratedValue NS  
Id S Use this annotation to make a field a primary key (or part of it).
IdClass NS Castor creates IdClass-like behaviour implicity when you define multiple Id fields. Castor does not support composed primary keys in relations!
Inheritance NS  
JoinColumn PS Supported: name
JoinColumns NS This is not supported because Castor does not support composed keys in relations.
JoinTable PS Supported: name, joincolumns, inverseJoincolumns
Lob S  
ManyToMany PS this is not tested properly yet.
ManyToOne PS Supported: targetEntity, fetch, optional, cascade - Relations MUST BE optional! Required relations are not supported.
MapKey NS  
MappedSuperclass NS  
NamedQuery S This annotation is used to specify a named query in OQL.
NamedQueries S This annotation specifies an array of named queries
NamedNativeQuery S This annotation is used to specify a native SQL named query.
NamedNativeQueries S This annotation specifies an array of named native queries.
OneToMany PS Supported: targetEntity, fetch, mappedBy, cascade
OneToOne PS Supported: targetEntity, fetch, optional, cascade - Relations MUST BE optional! Required relations are not supported.
OrderBy NS  
PersistenceContext NS  
PersistenceContexts NS  
PersistenceProperty NS  
PersistenceUnit NS  
PersistenceUnits NS  
PostLoad S  
PostPersist S  
PostRemove S  
PostUpdate S  
PrePersist S  
PreRemove S  
PreUpdate S  
PrimaryKeyJoinColumn NS  
PrimaryKeyJoinColumns NS  
QueryHint NS  
SecondaryTable NS  
SecondaryTables NS  
SequenceGenerator NS  
SqlResultSetMapping NS  
SqlResultSetMappings NS  
Table PS Supported: name
TableGenerator NS  
Temporal S  
Transient S  
UniqueConstraint NS  
Version NS  

6.5. Usage of JPA annotations - Configuration

This selection of HOW-TOs will show you how to persist JPA-annotated classes with Castor JDO, and will outline the required steps for each of the following cases:

  • Singular (stand-alone) entities

  • 1:1 relations

  • 1:M relations

  • M:N relations

6.5.1. HOW-TO persist a single class (@Entity, @Table, @Id)

The goal is to take an existing JPA-annotated class Single and persist it with Castor JDO. Let's first have a look at the domain class itself, first without JPA annotattions.

public class Single {
   private int id;
   private String name;
   
   public int getId() { ... }
   
   public void setId(int id ) { ... }
   
   public String getName() { ... }
   
   public void setName(String name) { ... }
}
        

Here's the same class again, this time with JPA annotations.

@Entity
@Table(name="mySingleTable")
public class Single {
   private int id;
   private String name;
   
   @Id
   @Column(name="id")
   public int getId() { ... }
   
   public void setId(int id) { ... }
   
   public String getName() { ... }
   
   public void setName(String name) { ... }
}
        

As shown, the class Single is mapped against the table mySingleTable, and its fields id and name are mapped to the columns id and name, where the column name for the id property is supplied explicitly and where the column name for the name property is derived from the property itself.

Next point is to create an DAO interface and its implementation where we will be using CastorDaoSupport from Castor's support for Spring ORM to implement the required methods.

public interface SingleDao {
	
    void save(Single single);
	
    Single get(int id);
	
    void delete(Single single);

}
   
public class SingleCastorDao extends CastorDaoSupport implements SingleDao {

    public void delete(Single single) {
        this.getCastorTemplate().remove(single);
    }

    public Single get(int id) {
        return (Single) this.getCastorTemplate().load(Single.class, new Integer(id));
    }

    public void save(Single single) {
        this.getCastorTemplate().create(single);
    }
}
   

There's one small final code change needed: For Castor to be able to work with JPA-annotated classes, you have to configure an instance of JDOClassDescriptorResolver and pass it to your JDOManager, else Castor won't be able to see those class files. Simply add the individual classes one by one or the package(s) as shown below:

JDOClassDescriptorResolver resolver = new JDOClassDescriptorResolverImpl();
resolver.addClass(org.castor.jpa.Single.class);
// or alternatively you can add the package:
resolver.addPackage("org.castor.jpa");
		
InputSource jdoConfiguration = ...;
JDOManager.loadConfiguration(jdoConfiguration, null, null, resolver);
		
JDOManager jdoManager = JDOManager.createInstance("jpa-extensions");
...
        

6.5.2. HOW-TO persist a 1:1 relation (@OneToOne)

The goal is to take the existing JPA-annotated classes OneToOne_A and OneToOne_B and persist them with Castor JDO. Let's first have a look at the domain classes themselves, this time with JPA annotations already in place.

@Entity
public class OneToOne_A {

   private int id;
   private String title;
   
   @Id
   @Column(name = "id")
   public int getId() { ... }
   
   public void setId(int id) { ... }
   
   @Column(name = "name")
   public String getTitle() { ... }
   
   public void setTitle(String title) { ... }
}
   
@Entity
@Table(name="OneToOne_B")
public class B {

   private int id;
   private String name;
   private OneToOne_A objA;
   
   @Id
   @Column(name = "id")
   public int getId() { ... }
   
   public void setId(int id) { ... }
   
   @Column(name = "name")
   public String getName() { ... }
   
   public void setName(String name) { ... }
   
   @OneToOne(optional=false)
   public OneToOne_A getOneToOne_A() { ... }
   
   public void setOneToOne_a(OneToOne_A objA) { ... }
}
        

As shown, the class OneToOne_A is mapped against the table OneToOne_A (implicit mapping), and the B against the table OneToOne-B (explicit mapping). Please note the @OneToOne annotation that specifies the 1:1 relation from class B to class OneToOne_A.

As with the example shown further above, do not forget to register all classes involved with the JDOClassDescriptorResolver as shown below:

JDOClassDescriptorResolver fragment:

resolver.addClass(org.castor.jpa.OneToOne_A.class);
resolver.addClass(org.castor.jpa.B.class);
        

or with the addPackage method:

resolver.addPackage("org.castor.jpa");
        

6.5.3. Persist one to many relation (@OnetoMany)

First we have to annotate our java classes.

    @Entity
    @Table(name="OneToMany_actor")
    public class Actor {
    
        private int svnr;
        private String lastname;
        private String firstname;
    
        @Id
        public int getSvnr() { ... }
        
	public void setSvnr(int svnr) { ... }
    
        @Column(name="surname")
        public String getLastname() { ... }
        public void setLastname(String lastname) { ... }
    
        @Column(name="firstname")
        public String getFirstname() { ... }
        public void setFirstname(String firstname) { ... }
    }
    
    @Entity 
    @Table(name="OneToMany_broadcast") 
    public class Broadcast { 
     
        private int id; 
        private String name; 
        private Collection<Actor> actors; 
     
        @Id 
        public int getId() { ... } 
	
        public void setId(int id) { ... } 
     
        public String getName() { ... } 
     
        public void setName(String name) { ... } 
     
        @OneToMany(targetEntity=Actor.class, mappedBy="actor_id") 
        public Collection<Actor>  getActors() { ... } 
     
        public void setActors(Collection<Actor> actors) { ... }
    } 
   

What you see is that with the small modification you can persist one to many relations easily.

Last don't forget to change your JDOClassDescriptorResolver accordingly.

6.5.4. HOW-TO create and use a named query (@NamedQuery)

The @NamedQuery annotation is used to specify a named query in castor's own query language (OQL) and it is expressed in metadata. The annotation takes the name and an OQL query as parameters.

To define a named query, we first need a persistence entity where we can attach the @NamedQuery annotation.

package your.package;

@Entity
@NamedQuery(name = "findPersonByName", 
            query = "SELECT p FROM your.package.Person p WHERE p.name = $1")
public class Person {

    private long id;
    private String name;

	@Id
	public long getId() {...}

	public void setId(final long id) {...}

	public String getName() {...}

	public void setName(final String name) {...}
}
        

As you can see, we defined a query using the name findPersonByName. The query itself uses $1 as a placeholder in its WHERE-clause, which must be bound when executing the query.

The following code sample illustrates how to execute the named query defined above:

Database db = jdoManager.getDatabase();

db.begin();
final OQLQuery query = db.getNamedQuery("findPersonByName");                           (1)
query.bind("Max Mustermann");                                                          (2)
final QueryResults queryResults = query.execute();                                     (3)
final Person queriedPerson = (Person) queryResults.next();
queryResults.close();
db.commit();
            

Let's have a closer look on some of the lines from this example.

1

... creates an OQL query using the above defined named query.

2

.. binds the placeholder $1 to a value.

3

... executes the query and handle the results.

6.5.5. HOW-TO create and use multiple named queries (@NamedQueries)

The @NamedQueries annotation is used to specifiy multiple named queries.

package your.package;

@Entity
@NamedQueries({
    @NamedQuery(name = "findPersonByName",
                query = "SELECT p FROM your.package.Person p WHERE p.name = $1"),
    @NamedQuery(name = "findPersonById",
                query = "SELECT p FROM you.package.Person p WHERE p.id = $1")
})
public class Person {

    private long id;
    private String name;

    @Id
    public long getId() {...}

    public void setId(final long id) {...}

    public String getName() {...}

    public void setName(final String name) {...}
}
        

In the obove example we defined two named queries, namly findPersonByName and findPersonById. The usage of each query is identical to the usasage of a single named query.

Database db = jdoManager.getDatabase();

db.begin();
final OQLQuery query = db.getNamedQuery("findPersonById");
query.bind(1000L);
final QueryResults queryResults = query.execute();
final Person queriedPerson = (Person) queryResults.next();
queryResults.close();
db.commit();
            

6.5.6. HOW-TO create and use a named native query (@NamedNativeQuery)

A named native query is a named query using native SQL syntax instead of castor's own query language. The handling of the annotation is similar to named queries.

First we need a entity to attach a query.

@Entity
@Table(name = personTable)
@NamedNativeQuery(name = "selectAllPersons",
                  query = "SELECT * FROM personTable")
public class Person {

    private long id;
    private String name;

	@Id
	public long getId() {...}

	public void setId(final long id) {...}

	public String getName() {...}

	public void setName(final String name) {...}
}
        

Although the query itself is written in native SQL syntax, we - again - use a OQLQuery object to execute the query.

Database db = jdoManager.getDatabase();

db.begin();
final OQLQuery query = db.getNamedQuery("selectAllPersons");
final QueryResults queryResults = query.execute();
... //process the results
queryResults.close();
db.commit();
        

6.5.7. HOW-TO create and use multiple named native queries (@NamedNativeQueries)

The @NamedNativeQueries annotation is used to specifiy multiple named native SQL queries.

package your.package;

@Entity
@Table(name = personTable)
@NamedNativeQueries({
    @NamedNativeQuery(name = "selectAllPersons",
                      query = "SELECT * FROM personTable"),
    @NamedNativeQuery(name = "findMustermann",
                      query = "SELECT * FROM personTable WHERE name='Max Mustermann' and id=1000")
})
public class Person {

    private long id;
    private String name;

    @Id
    public long getId() {...}

    public void setId(final long id) {...}

    public String getName() {...}

    public void setName(final String name) {...}
}
        

As we have already seen, the usage of the two above defined queries is equivalent to the usage of a single named native query.

Database db = jdoManager.getDatabase();

db.begin();
final OQLQuery query = db.getNamedQuery("findMustermann");
final QueryResults queryResults = query.execute();
final Person maxMustermann = (Person) queryResults.next();
queryResults.close();
db.commit();
        

6.5.8. HOW-TO use persistence callbacks

The following annotations can be used for handling persistence callbacks via JPA:

  • PostLoad
  • PrePersist
  • PostPersist
  • PreUpdate
  • PostUpdate
  • PreRemove
  • PostRemove

Additionally, there are the following listener-related annotations:

  • EntityListeners
  • ExcludeDefaultListeners
  • ExcludeSuperclassListeners

So, here's a basic usage example:

@Entity
public class Person {

    private final Log log = LogFactory.getLog(this.getClass());

    private long id;
    private String name;

    @Id
    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    @PostLoad
    protected void testPostLoadCallbackHooking() {
        log.debug(String.format("Hello from `PostLoad`. My name is %s.",
                this.name));
    }

    @PrePersist
    protected void validateCreation() {
        if (this.name.equals("Max Mustermann")) {
            throw new PersistenceException(String.format(
                    "Person mustn't be called %s.", this.name));
        }
    }

    @PostPersist
    protected void validatePersistence() {
        if (this.name.equals("Manfred Mustermann")) {
            throw new PersistenceException(String.format(
                    "Person shouldn't be called %s either.", this.name));
        }
    }

    @PreRemove
    protected void validateRemoval() {
        if (this.name.equals("Max Musterfrau")) {
            throw new PersistenceException(this.name + " mustn't be removed.");
        }
    }

    @PostRemove
    protected void validateDeletion() {
        if (this.name.equals("Manfred Musterfrau")) {
            throw new PersistenceException(this.name
                    + " shouldn't be removed either.");
        }
    }

    @PreUpdate
    protected void validateModification() {
        if (this.name.equals("Max Musterfrau")) {
            throw new PersistenceException(String.format(
                    "Person mustn't be renamed to %s.", this.name));
        }
    }

    @PostUpdate
    protected void validateUpdating() {
        if (this.name.equals("Hans Wurst")) {
            throw new PersistenceException(String.format(
                    "Person shouldn't be renamed to %s either.", this.name));
        }
    }

}
    

As one can see from this example, such callbacks can e.g. be used for handling validation based on CRUD (create, retrieve, update, delete) operation events.

Furthermore, there are possibilites to define listeners which allow for decoupling callback handling from entities.

Here's an example for that:

@Entity
@EntityListeners(DogListener.class)
public class Dog extends Animal {

    private long id;

    @Id
    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

}

// Corresponding listener.
public class DogListener {

    @PostPersist
    protected void postPersistDogListener() {
        // Do something.
    }

}
    

Apart from that, ExcludeDefaultListeners and ExcludeSuperclassListeners enable specifying exclusion of listeners within an inheritance chain of entities.

6.5.9. HOW-TO use @Enumerated

Enumerated can be used to persist Enum types.

Here's an example:

@Entity
public class EnumEntity {

    private long id;
    private StringEnum stringEnum;
    private OrdinalEnum ordinalEnum;

    @Id
    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    @Enumerated(STRING)
    public StringEnum getStringEnum() {
        return stringEnum;
    }

    public void setStringEnum(final StringEnum stringEnum) {
        this.stringEnum = stringEnum;
    }

    public OrdinalEnum getOrdinalEnum() {
        return ordinalEnum;
    }

    public void setOrdinalEnum(final OrdinalEnum ordinalEnum) {
        this.ordinalEnum = ordinalEnum;
    }

}
    

So, by default enums are serialized to their corresponding ordinal value representations for persistence. In this case, it's also sufficient to skip explicitly defining so via Enumerated. If serialization to respective string name representations is preferred annotating accordingly is required.

6.5.10. HOW-TO use @Temporal

This annotation can be used to specify properties mapped to temporal data structures.

Example:

@Entity
public class Person {

    private long id;
    private Date birthDate;
    private Date anotherDate;
    private Date yetAnotherDate;

    @Id
    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    @Temporal(TIMESTAMP)
    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(final Date birthDate) {
        this.birthDate = birthDate;
    }

    @Temporal(TIME)
    public Date getAnotherDate() {
        return anotherDate;
    }

    public void setAnotherDate(final Date anotherDate) {
        this.anotherDate = anotherDate;
    }

    @Temporal(DATE)
    public Date getYetAnotherDate() {
        return yetAnotherDate;
    }

    public void setYetAnotherDate(final Date yetAnotherDate) {
        this.yetAnotherDate = yetAnotherDate;
    }

}
    

So, it's possible to say which underlying DB-based field data structure to use (datetime, date or time).

6.5.11. HOW-TO use @Lob

Here's an example for that:

@Entity
public class LobEntity {

    private long id;
    private String clob;
    private byte[] blob;

    @Id
    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    @Lob
    public String getClob() {
        return clob;
    }

    public void setClob(final String clob) {
        this.clob = clob;
    }

    @Lob
    public byte[] getBlob() {
        return blob;
    }

    public void setBlob(final byte[] blob) {
        this.blob = blob;
    }

}
    

Consequently, default behavior here is to serialize to CLOB for character-based data and to BLOB for data based on byte arrays (i.e., files).

6.6. Integration with Spring ORM for Castor JDO

This guide will show you how to enable the use of JPA annotations with Castor JDO in the context of Spring, Spring ORM and the existing Spring ORM support for Castor JDO.

6.6.1. A typical sample

Let's look at a typical Spring configuration file that shows how to use Castor JDO with Spring as a Spring ORM provider.

spring-config.xml

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
          http://www.springframework.org/schema/tx 
          http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
    <!-- Enable transaction support using Annotations -->
    <tx:annotation-driven transaction-manager="transactionManager" />
    
    <bean id="transactionManager" 
          class="org.castor.spring.orm.CastorTransactionManager">
          <property name="JDOManager" ref="jdoManager" /> 
    </bean>

    <bean id="jdoManager"
        class="org.castor.spring.orm.LocalCastorFactoryBean">
        <property name="databaseName" value="dbName" />
        <property name="configLocation" value="jdo-conf.xml" />
    </bean>
    
    <bean id="singleDao" class="SingleCastorDao">
        <property name="JDOManager">
            <ref bean="jdoManager"/>
        </property>
    </bean>
</beans> 
        

Above Spring application context configures the following Spring beans:

  • A factory bean for JDOManager instantiation

  • A Castor-specific transaction manager.

  • The DAO implementation as shown above.

As shown above, the bean definition for the JDOManager factory bean points to a Castor JDO configuration file (jdo-conf.xml), whose content is shown below:

jdo-conf.xml

<!DOCTYPE jdo-conf PUBLIC "-//EXOLAB/Castor JDO Configuration DTD Version 1.0//EN""http://castor.org/jdo-conf.dtd">
<jdo-conf>
  <database name="dbName" engine="mysql">
     <driver url="jdbc:mysql://localhost:3306/single" 
             class-name="com.mysql.jdbc.Driver">
        <param name="user" value="user" />
        <param name="password" value="password" />
     </driver>
     <mapping href="mapping-empty.xml" />
  </database>
  <transaction-demarcation mode="local" />
</jdo-conf>
        

More on how to configure the Spring ORM provider for Castor JDO can be found at TBD.

6.6.2. Adding a JDOClassDescriptorResolver configuration

In order to use JPA-annotated classes with the Spring ORM provider for Castor JDO, you will have to use and configure a JDOClassDescriptorResolver through an additional bean definition and link it to your JDOManager bean factory definition.

<beans xmlns="http://www.springframework.org/schema/beans" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xmlns:tx="http://www.springframework.org/schema/tx"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
    ...

    <bean id="classDescriptorResolver"                                                 (1)
          class="org.castor.spring.orm.ClassDescriptorResolverFactoryBean">
        <property name="classes">
            <list>
                <value>org.castor.jpa.test.Single</value>
            </list>
        </property>
    </bean>

    ...
    
    <bean id="jdoManager"
        class="org.castor.spring.orm.LocalCastorFactoryBean">
        <property name="databaseName" value="dbName" />
        <property name="configLocation" value="jdo-conf.xml" />
        <property name="classDescriptorResolver" ref="classDescriptorResolver" />      (2)
    </bean>
    
    ...
</beans> 
            

where ....

1

Defines a JDOClassDescriptorResolver bean enlisting all the Java (domain) classes that carry JPA annotations.

2

links the JDOClassDescriptorResolver bean to the classDescriptorResolver property of the JDOManager bean definition.

If your domain classes share a set of packages, it is also possible to enlist those packages with the JDOClassDescriptorResolver bean, replacing the bean definition shown above as follows:

    <bean id="classDescriptorResolver"
          class="org.castor.spring.orm.ClassDescriptorResolverFactoryBean">
        <property name="packages">
            <list>
                <value>org.castor.jpa.test</value>
            </list>
        </property>
    </bean>
        

6.6.3. JPA Callbacks

In order to enable JPA callbacks handling via Spring ORM following exemplary config snippet is required:

<bean id="jdoManager" class="org.castor.spring.orm.LocalCastorFactoryBean">
    <property name="databaseName" value="testSimple" />
    <property name="configLocation"
        value="classpath:org/castor/jpa/scenario/callbacks/derby-jdo-conf.xml" />
    <property name="classDescriptorResolver" ref="classDescriptorResolver" />
    <property name="callbackInterceptor" ref="jpaCallbackHandler" />
</bean>

<bean id="jpaCallbackHandler" class="org.castor.jdo.jpa.info.JPACallbackHandler" />
        

6.7. Castor JPA Extensions

This section describes all JPA-extensions provided by Castor.

6.7.1. @Cache and @CacheProperty

In order to get the maximum out of the chosen built-in or external cache engine Castor provides a generic way to specify properties in a vendor-independent way. Castor allows for cache-tuning on a per-entity basis by simply providing key-value pairs with the @CacheProperty annotation in the @Cache container annnotation.

            @Entity
            @Cache({
                @CacheProperty(key="type", value="ehcache"),
                @CacheProperty(key="capacity", value="50")
            })
            @Table(name="Cache_limited")
            public class LimitedCachingEntity implements CacheTestEntity {
            
                private long id;
                private String name;
            
                @Id
                public long getId() {
                    return id;
                }
            
                public void setId(final long id) {
                    this.id = id;
                }
            
                public String getName() {
                    return name;
                }
            
                public void setName(final String name) {
                    this.name = name;
                }
            
            }
        

@Cache is based on Castor JDO and uses its default settings: 'count-limited' as cache type with a capacity of 30 entries.