## 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

Annotation | Supported | Comment
--- | --- | ---
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");
    query.bind("Max Mustermann");
    final QueryResults queryResults = query.execute();
    final Person queriedPerson = (Person) queryResults.next();
    queryResults.close();
    db.commit();

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

| ... creates an OQL query using the above defined named query.
| .. binds the placeholder `$1` to a value.
| ... 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"
              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" />
        </bean>

        ...
    </beans>

where ....

| Defines a `JDOClassDescriptorResolver` bean enlisting all the Java (domain) classes that carry JPA annotations.
| 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.

Prev | Home | Next
Chapter 5. Castor JDO - Integration with Spring ORM | The Castor community | Chapter 7. DDL generator for Castor JDO