Create a very simple working JPA persistence project to be understood and enable people to build in into a more complex solution. This article will concern mainly JPA client bundle configuration and we assume knowledge of OSGi platforms and JPA specification.
Note: this blog serves as my notepad, don’t take anything granted..
Apache Karaf ( Apache ServiceMix 4.4.1-fuse-08-15)
OSGi platform was originally intended to be very lightweight runtime container and a user could deploy and configure modules as needed. On the other hand – complex functionality, such as providing secure web services, data persistence, enterprise integration, etc requires set of modules playing together. So we advice to use an off-shelve bundled OSGi container with already prepared and tested features, such as – FuseSource, Talend Runtime, Apache ServiceMix, …
DerbyDB
A small and lightweight database, very good to start with.
OpenJPA Persistence
Apache OpenJPA is a Java persistence project at The Apache Software Foundation that can be used as a stand-alone POJO persistence layer or integrated into any Java EE compliant container and many other lightweight frameworks. We chose this framework for this example as it requires somehow less configuration comparing to EclipseLink (of cause – there’s a price of robustness and options to configure).
There is a nice overview of JPA framework options: tutorial-using-jpa-in-an-osgi-environment
Note:
Generally – there are 3 main frameworks to implement object persistence, in this article we will create a sample for Apache Aries JPA with OpenJPA Persistence Manager
There’s a rule to remember – DON’T MIX THEM!!!
In this example we will create 4 modules:
blogjpa project modules:
Note: blog-jpa is named because the example is intended for the blog
A module with entity classes, service interface and persistence unit definition. Person – an example entity
package com.apogado.blogjpa.commons;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
/**
* example entity object
* @author Gabriel Vince
*/
@Entity(name = "Person")
@NamedQuery(name = "Person.findById",query = "SELECT p FROM Person p WHERE p.id = :id")
public class Person implements Serializable {
public static final String QUERY_FIND_BY_ID = "Person.findById";
private Integer id;
private String name;
private String address;
@Id @GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "personname")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
PersonService – an example service interface
Persistence unit definition:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="blogjpa" transaction-type="RESOURCE_LOCAL" >
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<non-jta-data-source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/test)</non-jta-data-source>
<class>com.apogado.blogjpa.commons.Person</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="openjpa.Log" value="DefaultLevel=INFO, Tool=INFO"/>
<property name="openjpa.jdbc.DBDictionary" value="derby"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
</properties>
</persistence-unit>
</persistence>
Notes:
Important:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>blogjpa</artifactId>
<groupId>com.apogado.blogjpa</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>com.apogado.blogjpa</groupId>
<artifactId>blogjpa-commons</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>blogjpa-commons</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.7</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Meta-Persistence>META-INF/persistence.xml</Meta-Persistence>
<Export-Package>com.apogado.blogjpa.commons</Export-Package>
<Import-Package>
org.apache.openjpa.enhance,
org.apache.openjpa.util,
serp.*,
*
</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>compile</phase>
<configuration>
<tasks>
<path id="enhance.path.ref">
<fileset dir="${project.build.outputDirectory}">
<include name="Person.class" />
</fileset>
</path>
<pathconvert property="enhance.files" refid="enhance.path.ref" pathsep=" " />
<java classname="org.apache.openjpa.enhance.PCEnhancer">
<arg line="-p persistence.xml" />
<arg line="${enhance.files}" />
<classpath>
<path refid="maven.dependency.classpath" />
<path refid="maven.compile.classpath" />
</classpath>
</java>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
</project>
Implementation of the service interface. Simple and straightforward:
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://aries.apache.org/xmlns/jpa/v1.1.0"
xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0"
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint-2.8.0.xsd
http://aries.apache.org/xmlns/transactions/v1.0.0 http://aries.apache.org/schemas/transaction/transactionv10.xsd
http://aries.apache.org/xmlns/jpa/v1.1.0 http://aries.apache.org/schemas/jpa/jpa_110.xsd"
>
<bean id="personServiceBean" class="com.apogado.blogjpa.store.PersonServiceImpl" init-method="test" >
<jpa:unit property="entityManagerFactory" unitname="blogjpa" />
</bean>
<service interface="com.apogado.blogjpa.commons.PersonService" ref="personServiceBean"/>
</blueprint>
Note:
jpa:unit injects an EntityManagerFactory jpa:content injects an EntityManager
Example Service implementation
This module defines a Karaf feature – modules and their dependencies. Here I may deeply disappoint people doing things “lightly”, but as I wrote before, it is faster to use configured and tested features than fighting with dependencies.
In the resources there’s a link to knowhowlab.tips with exact dependency configuration and as well configuration for other persistence managers.
<?xml version="1.0" encoding="utf-8"?>
<features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0" name="ocm-features">
<feature name="blogjpa" version="1.0.0-SNAPSHOT">
<!-- feature url: mvn:com.apogado.blogjpa/blogjpa-feature/1.0.0-SNAPSHOT/xml/features -->
<!-- feature dependencies -->
<feature>jpa</feature>
<feature>http</feature>
<feature>jndi</feature>
<feature>transaction</feature>
<!-- openjpa library dependencies -->
<bundle>mvn:commons-collections/commons-collections/3.2.1</bundle>
<bundle>mvn:commons-pool/commons-pool/1.5</bundle>
<bundle>mvn:commons-dbcp/commons-dbcp/1.4</bundle>
<bundle>mvn:commons-lang/commons-lang/2.5</bundle>
<bundle>mvn:net.sourceforge.serp/com.springsource.serp/1.13.1</bundle>
<bundle>mvn:org.osgi/org.osgi.enterprise/5.0.0</bundle>
<bundle>mvn:org.osgi/org.osgi.compendium/4.3.0</bundle>
<!-- install openjpa -->
<bundle>mvn:org.apache.openjpa/openjpa/2.2.0</bundle>
<!-- install db client -->
<bundle>mvn:org.apache.derby/derbyclient/10.9.1.0</bundle>
<!-- application bundles -->
<bundle>mvn:com.apogado.blogjpa/blogjpa-commons/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:com.apogado.blogjpa/blogjpa-datasource/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:com.apogado.blogjpa/blogjpa-store/1.0.0-SNAPSHOT</bundle>
</feature>
</features>
and pom.xml
Build a project – here we assume a common maven repository between build maven and Apache Karaf. If the repositories are not the same, you need to configure the maven repository in the Karaf environment (org.ops4j.pax.url.mvn.cfg)
features:addurl mvn:com.apogado.blogjpa/blogjpa-feature/1.0.0-SNAPSHOT/xml/features
features:install -c -v blogjpa
15:38:47,831 | INFO | rint Extender: 1 | PersonServiceImpl | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | running
15:38:47,831 | INFO | rint Extender: 1 | PersonServiceImpl | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | entity manager factory available
15:38:57,417 | INFO | rint Extender: 1 | PersonServiceImpl | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | Created Person id 1
15:38:57,447 | INFO | rint Extender: 1 | PersonServiceImpl | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | Person persisted with id: 1
15:38:57,597 | INFO | rint Extender: 1 | PersonServiceImpl | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | Person found with name: a9f0ed64-afaa-4e3c-9c5d-72c94424cbc3
15:38:57,597 | INFO | rint Extender: 1 | PersonServiceImpl | 90 - com.apogado.blogjpa.store - 1.0.0.SNAPSHOT | Person found with name: a9f0ed64-afaa-4e3c-9c5d-72c94424cbc3
Resources:
example project: blogjpa