
Using @DataSourceDefinition in Java EE 6 With Postgresql
Contents
Introduction
Java EE applications use DataSource
objects when they access relational databases through the JDBC API. Typically the Datasource
that should be used is referenced by its Java Naming and Directory Interface (JNDI) name in the persistence.xml
file:
> java:app/env/myDatasource>
The Datasource
details (host, username, password, database type) for this JNDI name need to be declared somewhere. Prior to Java EE 6, you could create a DataSource object using vendor-specific mechanisms. This could be a deployment descriptor for the application server in the WAR, e.g. glassfish-web.xml
or XML configuration files in the application server directory.
In Java EE 6 the @DataSourceDefinition annotation was introduced. With this annotation you can declare ‘application server agnostic’ datasources directly in your code. While this is usually a bad idea for production code, it can be very useful for testing, since you don’t have to change the configuration of the application server. You can just deploy the WAR in any application server and it will run.
In this example we run through the steps for setting up a JPA project with a Postgresql database connection using the @DataSourceDefinition
annotation.
1. Install Postgresql Driver in Application Server
First, make sure that the application server is set up to support Postgresql connections. For Glassfish 3 we need to install the Postgresql JDBC drivers.
- Download the latest JDBC drivers at the Postgresql website.
- Download the driver file
postgresql-.jdbc4.jar
and copy it to
./glassfish/lib/
For other application servers refer to the documentation.
2. Define Datasource With @DataSourceDefinition Annotation
Next we define the details of the datasource named java:app/env/myDatasource
using the @DataSourceDefinition
annotation in the Java code. We create a special class for this purpose, in DbConfiguration.java:
import javax.annotation.sql.DataSourceDefinition; @DataSourceDefinition(name = "java:app/env/myDatasource", minPoolSize = 0, initialPoolSize = 0, className = "org.postgresql.ds.PGSimpleDataSource", serverName="localhost", user = "admin", password = "admin", databaseName = "test" ) public class DbConfiguration { }
When the application server deploys the application, all @DataSourceDefinition
annotations are processed. The application server will look in all classes, so it doesn’t matter where you put the definition.
Note: you can also define a datasource in the web.xml
file that is included in the WAR file.:
> > java:app/env/myDatasource>> org.postgresql.ds.PGSimpleDataSource>> localhost>> 5432>> test>> admin>> admin> >
3. Add Datasource to persistence.xml
Our persistence.xml is quite simple, referencing the data source named java:app/env/myDatasource
with the JTA transaction type.
version="1.0" encoding="UTF-8"?>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"> name="myUnit" transaction-type="JTA"> > java:app/env/myDatasource> > >
Using the Datasource
Now we can make a simple JSF 2.0 web application that uses the database. Let’s make a simple entity with a ‘name’ field.
NamedEntity.java
import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.NamedQuery; import javax.persistence.Table; @Entity @NamedQuery(name="getAll", query="SELECT c from NamedEntity c") public class NamedEntity implements Serializable { private static final long serialVersionUID = 1L; @Id private String name; public String getName() { return this.name; } public void setName(String name) { this.name = name; } }
And we add NamedEntity to our persistence unit:
name="myUnit" transaction-type="JTA"> > java:app/env/myDatasource>> NamedEntity> >
We also need a bean that retrieves the names, and makes them available in the JSF page. Since we have declared transaction-type="JTA"
in persistence.xml
, we can use the @PersistenceContext
annotation. This tells the application server to inject a reference to the appropriate EntityManager instance at run-time. In this case it means we will get an EntityManger for the myUnit
persistence unit.
TestBean.java
import java.io.Serializable; import java.util.List; import javax.annotation.sql.DataSourceDefinition; import javax.faces.bean.ApplicationScoped; import javax.inject.Named; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; @Named("test") @ApplicationScoped public class TestBean implements Serializable { private static final long serialVersionUID = 1L; // EntityManager is injected here by application server @PersistenceContext private EntityManager em; public String getNames() { TypedQuery<NamedEntity> query = em.createNamedQuery("getAll", NamedEntity.class); List<NamedEntity> results = query.getResultList(); StringBuilder result = new StringBuilder("The name are: ["); String separator = ""; for (NamedEntity entity: results) { result.append(separator).append(entity.getName()); separator = ", "; } result.append("]"); return result.toString(); } }
Then in our JSF page we can reference the TestBean class through an EL expression:
value="#{test.getNames()}"/>
This code should work without problems, printing all rows from the database, e.g.:
The names are: [Jack, John, Dave]
Common Glassfish Problems
Some errors you may run into when using Glassfish 3 are:
WebappClassLoader unable to load resource
javax.servlet.ServletException: WEB9031: WebappClassLoader unable to load resource [org.postgresql.jdbc4.Jdbc4PreparedStatement], because it has not yet been started, or was already stopped
This means the Postgresql JDBC driver cannot be found. Copy the JDBC JAR file to
and restart the server.
No PasswordCredential found
WARNING: RAR5038:Unexpected exception while creating resource for pool __SYSTEM/pools/__datasource_definition/testApp/java:app/env/myDatasource. Exception : javax.resource.spi.SecurityException: No PasswordCredential found WARNING: RAR5117 : Failed to obtain/create connection from connection pool [ __SYSTEM/pools/__datasource_definition/testApp/java:app/env/myDatasource]. Reason : com.sun.appserv.connectors.internal.api.PoolingException: No PasswordCredential found WARNING: RAR5114 : Error allocating connection : [Error in allocating a connection. Cause: No PasswordCredential found]
This error indicates the password field for a datasource was left blank. Even though it’s perfectly valid to connect to a database without a password, Glassfish does not like this — it will completely ignore empty properties. Thankfully there is a special meta-string that can be used instead property values to represent a zero length string: ()
.
ERROR: relation ‘namedentity’ does not exist
Internal Exception: org.postgresql.util.PSQLException: ERROR: relation "namedentity" does not exist Error Code: 0 Call: INSERT INTO NAMEDENTITY (NAME) VALUES (?) bind => [1 parameter bound] Query: InsertObjectQuery(my.package.namedentity@7f96dfa4)
This means the table associated with an entity does not exist in the database yet. You can tell the JPA implementation to automatically create the tables, but since this is outside the spec there is no fixed syntax. For EclipseLink (the JPA implementation inside Glassfish 3) you have to add two properties to your persistence.xml
file:
name="myUnit" transaction-type="JTA">
name="eclipselink.ddl-generation" value="create-tables" />
name="eclipselink.ddl-generation.output-mode" value="database" />
>