2/06/2011

EJB lite, JPA, DataSource embedded in java application

This post demonstrates how to put EJB lite, JPA, DataSource together in a standalone java application, using standard JavaEE 6 API in GlassFish. The application source consists of 4 files: persistence.xml, JPA entity, no-interface EJB, and main class.

Transaction is managed by embeddable EJB container. The persistence unit is backed by an application-scoped JTA DataSource declared with annotation on EJB class. No need to package or deploy anything. Client and server pieces are all in a single JVM.

classes/META-INF/persistence.xml :

<?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="test_pu" transaction-type="JTA">
<jta-data-source>java:app/jdbc/test</jta-data-source>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
<property name="eclipselink.logging.level" value="FINE"/>
</properties>
</persistence-unit>
</persistence>
JPA entity Employee:
package test;
import javax.persistence.*;

@Entity
public class Employee implements java.io.Serializable {
private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Basic private String name;

public Long getId() {
return id;
}

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

public String getName() {
return name;
}

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

@Override public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}

@Override public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Employee)) {
return false;
}
Employee other = (Employee) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}

@Override public String toString() {
return "Employee id=" + id + ", name=" + name;
}
}
No-interface stateless EJB:
package test;
import javax.ejb.Stateless;
import javax.persistence.*;
import javax.annotation.sql.DataSourceDefinition;

@Stateless
@DataSourceDefinition(
name="java:app/jdbc/test",
className="com.mysql.jdbc.jdbc2.optional.MysqlDataSource",
user="root",
password="root",
databaseName="test",
serverName="localhost",
portNumber=3306 )
public class TestBean {
@PersistenceContext private EntityManager em;

public void addEmployee(String[] names) {
for(String name : names) {
Employee e = new Employee();
e.setName(name);
em.persist(e);
}
}
}
main class:
package test;
import javax.ejb.embeddable.EJBContainer;
import javax.naming.*;

public class Client {
public static void main(String[] args) throws Exception {
EJBContainer container = EJBContainer.createEJBContainer();
Context namingContext = container.getContext();
TestBean testBean = (TestBean) namingContext.lookup("java:global/classes/TestBean");
testBean.addEmployee(args); //need to pass in employee names from command line
container.close();
}
}
To compile from project directory:
$ javac -cp "classes:$GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar" classes/test/*java
Start mysqld, and run the app:
cd /usr/local/mysql-5.1.32; sudo bin/mysqld_safe --user root
java -cp "classes:$GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar:$HOME/mysql-connector-java-5.1.5-bin.jar" test.Client Jon Jane
Verify entities are successfully created by running mysql:
mysql> select * from EMPLOYEE;
+----+------+
| ID | NAME |
+----+------+
| 1 | Jon |
| 2 | Jane |
+----+------+
2 rows in set (0.00 sec)
If you are wondering why the jar is named xxx-static-shell.jar, I guess "static" means it's not OSGi bundle, and "shell" means it's a wrapper for all the other GlassFish jars.

7 comments:

JSampo said...

Hi,

thanx for this tutorial.

I just start with JEE6, so I try this tutorial.
But I got exception below.
I have installed GF3, postgresql 9.0.
And I have postgresql-9.0-801.jdbc4.jar in
..\glassfish3\glassfish\lib\ext\postgresql-9.0-801.jdbc4.jar
..\glassfish3\glassfish\domains\domain1\lib\postgresql-9.0-801.jdbc4.jar
and
..\glassfish3\glassfish\domains\domain1\lib\ext\postgresql-9.0-801.jdbc4.jar
And I added
@DataSourceDefinition(
name = "java:app/jdbc/testDS",
...
url = "jdbc:postgresql://localhost:5432/postgres") // URL
But still doesn't work.
Any idea what is wrong?

Thanx for answer.

.

WARNING: RAR5038:Unexpected exception while creating resource for pool __SYSTEM/pools/__datasource_definition/./java:app/jdbc/testDS. Exception : javax.resource.spi.ResourceAllocat
ionException: The connection could not be allocated: The url cannot be null
9.5.2011 1:39:48 com.sun.enterprise.connectors.ConnectionManagerImpl internalGetConnection
WARNING: RAR5117 : Failed to obtain/create connection from connection pool [ __SYSTEM/pools/__datasource_definition/./java:app/jdbc/testDS ]. Reason : com.sun.appserv.connectors.in
ternal.api.PoolingException: The connection could not be allocated: The url cannot be null
9.5.2011 1:39:48 com.sun.gjc.spi.base.DataSource logNonTransientException
WARNING: jdbc.exc_get_conn
. . . .
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Error in allocating a connection. Cause: The connection could not be allocated: The url cannot be null
Error Code: 0
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:309)
at org.eclipse.persistence.sessions.JNDIConnector.connect(JNDIConnector.java:138)
at org.eclipse.persistence.sessions.JNDIConnector.connect(JNDIConnector.java:94)
at org.eclipse.persistence.sessions.DatasourceLogin.connectToDatasource(DatasourceLogin.java:162)
at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.loginAndDetectDatasource(DatabaseSessionImpl.java:592)
at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:233)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:394)
... 32 more

javahowto said...

I haven't tried postgres yet. Some wild guess:

1, In @DataSourceDefinition, try adding url attribute and removing databaseName, serverName and portNumber attributes as these props are already included in url.

2, if 1 doesn't work, then try the following:

@DataSourceDefinition(
name="java:app/jdbc/testDS",
className="...",
user="...",
...
databaseName="postgres",
serverName="localhost",
portNumber=5432,

properties={"url=jdbc:postgresql://localhost:5432/postgres"}

3, if 2 doesn't work, then do not define datasource inside your app by removing @DataSourceDefinition. Instead, define the data source in GlassFish admin console http://localhost:4848. You need to change its jndi name from java:app/jdbc/testDS to just testDS, or jdbc/testDS. Also update persistence.xml w/ the new name.
)

JSampo said...

Hi,

thank You.

I tried
properties={"url=jdbc:postgresql://localhost:5432/postgres"}

and it works :)

Best regards.

Anonymous said...

Has anyone tried this on JBoss AS 6.1? I tried but can't make persistence.xml use a Datasoure defined by an annotation.

javahowto said...

Using the application-scoped data-source defined with @DataSourceDefinition in persistence.xml can be tricky to implement in appserver. Not sure if this is already supported in JBoss 6.1.

Anna said...

Great and Useful Article.

J2EE Training

Anna said...

Great and Useful Article.

Online Java Course

Java Online Training

Java Course Online

J2EE training

online J2EE training

Best Recommended books for Spring framework

Java Interview Questions












Java Training Institutes in Chennai

Java Training in Chennai

J2EE Training in Chennai

java j2ee training institutes in chennai