XAP8 == "XAP9NET" || XAP8 == "XAP9" || XAP8 == "XAP9NET")

JPA Support

You are viewing an old version (v. 21) of this page.
The latest version is v. 56, last edited on Feb 03, 2012 (view differences | )
<< View previous version | view page history | view next version >>


Search XAP 8.0
Searching XAP 8.0 Documentation
Browse XAP 8.0
Offline Documentation

Download latest offline documentation
in HTML format:

xap-8.0-documentation.zip (15MB)

                                                              

Overview

The Java Persistency API (JPA) is a Java programming language framework managing relational data in applications using Java Platform.
GigaSpaces JPA implementation is based on OpenJPA 2.0.0.

Configuration

The persistence.xml

GigaSpaces JPA persistence.xml has 3 mandatory properties that should always be set:

  1. BrokerFactory should be set to "abstractstore" which tells OpenJPA that an alternate Store Manager (the layer responsible for interaction with DB) is going to be used.
  2. abstractstore.AbstractStoreManager should be set to "org.openspaces.jpa.StoreManager" which tells OpenJPA to use OpenSpaces store manager.
  3. LockManager should be set to "none" since OpenJPA's default lock manager is set to "version" (Optimistic locking) which is currently unsupported.

Your persistence.xml file should be placed in any **/META-INF folder in your classpath.

Here's an example of a persistence.xml configuration file:

<persistence-unit name="gigaspaces" transaction-type="RESOURCE_LOCAL">
	<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
	<properties>
           <property name="BrokerFactory" value="abstractstore"/>
           <property name="abstractstore.AbstractStoreManager" value="org.openspaces.jpa.StoreManager"/>
           <property name="LockManager" value="none"/>
	</properties>	
</persistence-unit>

Specifying a Space URL can be done using the ConnectionURL property as follows:

<!-- Embedded Space -->
<property name="ConnectionURL" value="/./jpaSpace"/>

<!-- Remote Space -->
<property name="ConnectionURL" value="jini://*/*/jpaSpace"/>

Detailed information regarding persistence.xml can be found on OpenJPA's Manual.

Listing Your Persistent Classes

Information regarding Entities declaration can be found on JPA Support#GigaSpaces JPA Entities.

One of JPA's requirements is to list your persistent classes in persistence.xml file.
This is done by using the <class> tag.
For example, if i'm going to use the classes: Trade, Book & Author i'll list them in persistence.xml in the following way:

<persistence-unit name="gigaspaces" transaction-type="RESOURCE_LOCAL">
	<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <class>org.openspaces.objects.Trade</class>
        <class>org.openspaces.objects.Book</class>
        <class>org.openspaces.objects.Author</class>
	<properties>
           <property name="BrokerFactory" value="abstractstore"/>
           <property name="abstractstore.AbstractStoreManager" value="org.openspaces.jpa.StoreManager"/>
           <property name="LockManager" value="none"/>
	</properties>	
</persistence-unit>

Enhancing Your Classes

JPA classes are monitored in runtime and therefore requires enhancement.
OpenJPA offers 3 ways of doing so:

  1. Enhance at build time using an Ant script (can also be used with maven).
  2. Enahnce at runtime by using OpenJPA's javaagent enhancer.
  3. Subclasses runtime enhancement (not recommended).
    Detailed information regarding how to enhance your entities can be found on OpenJPA's Entity Enhancement page.

Example

In this stage, we have a runnable JPA environment.
The following example shows how to persist, find, update & remove an entity using JPA:

// Create a factory for our 'gigaspaces' persistence-unit
EntityManagerFactory emf = Persistence.createEntityManagerFactory("gigaspaces");

// Create an entity manager
EntityManager em = emf.createEntityManager();

// Find entity
Trade trade = (Trade) em.find(Trade.class, 12345L);

// If not found, persist a new instance
if (trade == null) {
    // Persist entity
    trade = new Trade();
    trade.setId(12345L);
    trade.setQuantity(75);
    em.getTransaction().begin();
    em.persist(trade);
    em.getTransaction().commit();
}

// Update entity
em.getTransaction().begin();
trade.setQuantity(100);
em.getTransaction().commit();

// Remove entity
em.getTransaction().begin();
em.remove(trade);
em.getTransaction().commit();

em.close();
emf.close();

Its also possible to specify a Space proxy instance to use when creating an EntityManagerFactory in the following way:

GigaSpace gigaspace = ...
Properties properties = new Properties();
properties.put("ConnectionFactory", gigaspace.getSpace());
EntityManagerFactory emf = Persistence.createEntityManagerFactory("gigaspaces", properties);

Configuration using Spring

It is possible to inject either an EntityManager or EntityManager factory using Spring.
In the following example we'll see how to inject a space and an EntityManagerFactory.
In the following Spring xml configuration file we'll declare a space, an entity manager factory, a transaction manager and a JPA service bean:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:os-core="http://www.openspaces.org/schema/core"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.openspaces.org/schema/core http://www.openspaces.org/schema/core/openspaces-core.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
       
	<!-- space definition -->
        <os-core:space id="space" url="/./jpaSpace" lookup-groups="test"/>

        <!-- entity manager factory definition -->
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="spring"/>
		<property name="jpaVendorAdapter">
			<bean class="org.openspaces.jpa.OpenSpacesJpaVendorAdapter">
				<property name="space" ref="space"/>
			</bean>
		</property>
	</bean>	

        <!-- transaction manager definition -->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>		

        <!-- support annotations -->
	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />	
	<context:annotation-config/>	
	<tx:annotation-driven transaction-manager="transactionManager"/>

        <!-- our JPA service definition -->
	<bean id="jpaService" class="org.openspaces.jpa.JpaService" />
</beans>

Here's our JpaService implementation:

@Repository
@Transactional
public class JpaService {
    @PersistenceContext
    private EntityManager em;
    
    public JpaService() {
    }
               
    @Transactional
    public void persistObject() {            
        em.persist(...);            
    }
}

GigaSpaces JPA Entities

An entity class must meet the following requirements:

  1. The class must be annotated with javax.persistence.Entity annotation.
  2. The class must have a public no-argument constructor (The class may have other constructors).
  3. GigaSpaces and JPA annotations can only be declared on Getters, NOT on fields.

Annotations

GigaSpaces JPA Entities must have both JPA and GigaSpaces annotations for the following annotations:

GigaSpaces JPA
@SpaceId @Id/@EmbeddedId
@SpaceExclude @Transient

GigaSpaces annotations which don't require JPA annotations:

GigaSpaces Annotation
@SpaceRouting
@SpaceIndex

Here's an example of a basic Entity:

@Entity
public class Trade {
  private Long id;
  private Double quantity;
  private List<Double> rates;
  private boolean state;

  // Public no-argument constructor
  public Trade() {
  }

  // Both SpaceId and Id should be declared on the id property
  @Id
  @SpaceId
  public Long getId() {
    return this.id;
  }
 
  // Persistent property, no additional GigaSpaces annotations needs to be used.
  @Basic
  public Double getQuantity() {
    return this.quantity;
  }

  // A persistent collection property. In this case we'll use a GigaSpaces annotation
  // for indexing its values.
  @ElementCollection
  @SpaceIndex(path = "[*]")
  public List<Double> getRates() {
    return this.rates;
  }
  
  // A transient property. In this case we'll use both GigaSpaces and JPA annotations.
  @Transient
  @SpaceExclude
  public boolean getState() {
    return this.state;
  }

  /* Additional Getters & Setters... */

}

For auto generated Id declaration and Complex object Id declaration refer to JPA Entity Id.

Here's an example of a JPA Owner entity:

@Entity
public class Owner {
    //
    private Integer id;
    private String name;
    private List<Pet> pets;    
    //    
    public Owner() {
    }
    public Owner(Integer id, String name, List<Pet> pets) {
        super();
        this.id = id;
        this.name = name;
        this.pets = pets;
    }
    //   
    @Id
    @SpaceId
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    @Basic
    @SpaceRouting
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @OneToMany(cascade = CascadeType.ALL)
    public List<Pet> getPets() {
        return pets;
    }
    public void setPets(List<Pet> pets) {
        this.pets = pets;
    }
}

JPA Query Language (JPQL)

Here's an example of how to query customers from a specific country:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("gigaspaces");
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("SELECT c from org.openspaces.objects.Customer c WHERE c.address.country = 'United States'");
List<Customer> customers = (List<Customer>) query.getResultList();
em.close();
emf.close();

The following example demonstrates how to select an Owner with a specific Pet using an INNER JOIN:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("gigaspaces");
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("SELECT o FROM org.openspaces.objects.Owner o JOIN o.pets p WHERE p.name = :name");
query.setParameter("name", "Whiskey");
Owner owner = (Owner) query.getSingleResult();
em.close();
emf.close();
When specifying entity names in GigaSpaces JPQL the full class qualified name should be used as shown in the previous example.

Persisting Collection Properties

It's possible to make a collection property persistent using the @ElementCollection annotation.
In the following example we have an entity with a collection of Integers:

@Entity
public class Card {
  // ...
  
  private List<Integer> numbers;
  
  @ElementCollection
  public List<Integer> getNumbers() {
    return this.numbers;
  }
  
  public void setNumbers(List<Integer> numbers) {
    this.numbers = numbers;
  }
  
  // ...
}

In order to query the Card entity using a specific Integer in the numbers collection we use JPQL "MEMBER OF":

EntityManagerFactory emf = Persistence.createEntityManagerFactory("gigaspaces");
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("SELECT c FROM org.openspaces.objects.Card c WHERE :number MEMBER OF c.numbers");
query.setParameter("number", "10");
Card card = (Card) query.getSingleResult();
em.close();
emf.close();

Persisting Enum Properties

JPA allows persisting Enum proeprties using the @Enumerated annotation.
Lets examine the following example:

// A Vehicle entity which has an Enum property
@Entity
public class Vehicle {
  // Enum Declaration
  public enum VehicleType { CAR, TRUCK, BIKE };
  
  private Integer id;
  private String name;
  private VehicleType type;

  public Vehicle() {
  }

  @Id
  @SpaceId
  public Integer getId() {
    return this.id;
  }

  @Basic
  public String getName() {
    return this.name;
  }
  
  @Enumerated
  public VehicleType getType() {
    return this.type;
  }

  /* Additional Getters & Setters */

}

We used the Enumerated annotation for persisting an Enum property.
Please note that using the Enumerated.value attribute has no effect since Enums are saved in GigaSpaces as is.

Enums In JPQL

It's possible to query according to an Enum property by setting an Enum parameter or by using the Enum's value in the query's string:

EntityManager em = emf.createEntityManager();

// Query using an Enum parameter
Query query1 = em.createQuery("SELECT vehicle FROM com.gigaspaces.objects.Vehicle vehicle WHERE vehicle.type = :type");
query1.setParameter("type", VehicleType.CAR);
Vehicle result1 = (Vehicle) query1.getSingleResult();

// Query using an Enum in query's string
Query query2 = em.createQuery("SELECT vehicle FROM com.gigaspaces.objects.Vehicle vehicle WHERE vehicle.type = 'BIKE'");
Vehicle result2 = (Vehicle) query2.getSingleResult();

GigaSpaces JPA Limitations

For a list of unsupported JPA features and limitations please refer to [JPA Unsupported Features].

Labels

 
(None)