Getting Started with EJB 3.0 Persistence out-of-container using the Reference Implementation (GlassFish)

In this article I will report on my very first steps with EJB 3.0 Persistence in a J2SE application – that is: outside the context of the J2EE Container. In a previous post, EJB 3.0 Reference Implementation with teeth – Open Source EJB 3.0 by Sun and Oracle (Report from JavaPolis), I have introduced EJB 3.0 – based partly on the presentation by Mike Keith, Co-Specification Lead for JSR-220 (EJB 3.0) and member of the Expert Group for JSR-244 (JEE 5) and Architect for Oracle TopLink and the EJB Container in OC4J – the J2EE Application Server from Oracle.

In this post, I will download and install GlassFish – the Reference Implementation for EJB 3.0. Next I will write the almost simplest J2SE Application that will make use of EJB 3.0 Persistence to communicate with a relational database. I will run this application ‘stand-alone’ – not in context of any container, neither Web Server/Servlet Container nor J2EE (EJB) Container. In a subsequent post, I will talk about running the application from Oracle JDeveloper 10.1.3 EA (Early Adopter’s Release) – in such a way that there are only dependencies on GlassFish, not on any proprietary Oracle classes.

Following my first careful steps, you should be able to get EJB 3.0 Persistence up and running for yourself in less than two hours.

The steps are:

  • Download and Set Up Java 5 (JDK 5.0/JRE 5.0)
  • Download and Set Up GlassFish
  • Create Entities – POJOs with annotations that link them to database objects
  • Create a Business Service class that provides Entitiy Services (like an out-of-container Session Bean)
  • Create a Client that leverages the Business Service to do stuff
  • Run Application

Download and Set Up Java 5 (JDK 5.0/JRE 5.0)

If you have not already set up Java 5, that is the first thing you probably should do as GlassFish (and EJB 3.0) requires Java 5.0 as JRE. Go to the Sun Downloadsite. I downloaded JDK 5.0 Update 6. Run the installer. It is probably convenient to add the JDK50_HOME\bin directory to the PATH environment variable – to ensure that when you type java or javac on the command prompt, it actually refers to JDK 5.0. In my case, %PATH% now starts with: C:\Java\jdk1.5.0_06\bin.

You can check whether you are using the correct JDK by typing java -version on the command prompt. It should return with something like:

java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)

Download and Set Up GlassFish

GlassFish can be found at the GlassFish project page on dev.java.net. There is no formal Production Release to be downloaded. You can find milestones, promoted builds and nightly builds. It is clear that GlassFish is still very much on the move. For getting started, you do not need the absolute latest features and you also do not need production level robustness, so I suggest – as I did – that you download one of the promoted builds. Click on the Download Now button and pick the latest promoted build. In my case that was promoted binary build 21 december 2005 (60Mb).

To install I did the following:

  1. save the downloaded jar file in c:\glassfish
  2. open a command window and navigate to c:\glassfish
  3. type in: java -Xmx256m -jar glassfish-installer-9.0-b32.jar
  4. type in: C:\glassfish\glassfish\lib\ant\bin\ant -f setup.xml

On the first run of step 4, I learned from the feedback that my port 8080 was already used, so I went into the setup.xml file to change the instance.port property to 8085. In this setup.xml file – in $GlassFish_HOME, which was in my case c:\glassfish\glassfish – you can find other properties to specify port and username/password for the GlassFish admin-console, imq, orb and https port numbers as well as the default domain name. Unless you have port-conflicts, you do not need to change anything in this file.

Follow the instructions in the Quick Start Guide (https://glassfish.dev.java.net/downloads/quickstart/index.html) to verify the successful installation:

  • Add the c:\glassfish\glassfish\bin directory to the PATH environment variable.
  • Start the server by entering this command: asadmin start-domain domain1.

When the server has started, this message appears: Domain domain1 is ready to receive client requests. Additional services are being started in the background.

  • Point the browser to: http://localhost:8085/ (in my case; in your case this probably is http://localhost:8080/ if you did not change the default port).
  • Now the server should come up with a static document: Sun Java System Application Server Platform Edition 9.0 – Your server is up and running!

 

  • Login to the GlassFish admin-console: http://localhost:4848/. You can connect using username/password (default): admin/adminadmin (you can set these in the setup.xml file before installing GlassFish.
  • Download and run Hello World demo-application: Download hello.war (from https://glassfish.dev.java.net/downloads/quickstart/hello.war) and copy it to C:\glassfish\glassfish\domains\domain1\autodeploy. The war file is automatically picked up by the GlassFish Server and deployed. Try to run the application: http://localhost:8085/hello. It works: I get prompted for my name and are subsequently welcomed in a friendly manner (Hello, Lucas!)

Getting Started with EJB 3.0 Persistence out-of-container using the Reference Implementation (GlassFish) glassfish hello

  • Stop the server: asadmin stop-domain domain1. For an out-of-container trial of EJB 3.0 Persistence, I really only need some libraries that are part of GlassFish, I do not need the Container up and running!

By the way: it sort of becomes clear now what an ulterior motive might be for both Sun and Oracle to provide large chunks of their commercial products, the Sun Application Server as well as Oracle’s TopLink O/R Framework, for free in the Reference Implementation: once you get really started with the RI, the ways of working of the AppServer as well as TopLink are quite familiar when you might decide to go for a commercial product that provides more functionality. It will be an easy step to the Sun and Oracle products. And of course the RI is a great showcase as well as formidable beta-test of the Sun and Oracle technology!

Create Entities – POJOs with annotations that link them to database objects

I am really anxious to find out about out-of-container persistence with EJB 3.0. This is described for Glassfish on: GlassFish Project – Entity Persistence Support. For some reason, the zip-file with example code that is mentioned on this page fails to download. Well, the essential code samples are in this page, so I will manage.

I create two extremely simple POJOs, that I decorate with EJB 3.0 annotations to instruct the EJB 3.0 EntityManager about mapping them to database tables. Note: this is really the simplest set of annotations, just to get the show going. For the umptieth time I make use of Department and Employee, the quintessential domain objects as they represent the age-old demo schema in Oracle databases.

package nl.amis.ejb30.hrm;

import java.io.Serializable;

import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="EMP")
public class Employee implements Serializable {
    private Double comm;

    private Long deptno;

    private Long empno;

    private String ename;

    private Timestamp hiredate;

    private String job;

    private Long mgr;

    private Double sal;

    public Employee() {
    }

    public Employee(Long empno) {
        this.empno = empno;
    }

    @Column(name="COMM")
    public Double getComm() {
        return comm;
    }

    public void setComm(Double comm) {
        this.comm = comm;
    }

    @Column(name="DEPTNO")
    public Long getDeptno() {
        return deptno;
    }

    public void setDeptno(Long deptno) {
        this.deptno = deptno;
    }

    @Id
    @Column(name="EMPNO", nullable=false)
    public Long getEmpno() {
        return empno;
    }

    public void setEmpno(Long empno) {
        this.empno = empno;
    }

    @Column(name="ENAME")
    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    @Column(name="HIREDATE")
    public Timestamp getHiredate() {
        return hiredate;
    }

    public void setHiredate(Timestamp hiredate) {
        this.hiredate = hiredate;
    }

    @Column(name="JOB")
    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    @Column(name="MGR")
    public Long getMgr() {
        return mgr;
    }

    public void setMgr(Long mgr) {
        this.mgr = mgr;
    }

    @Column(name="SAL")
    public Double getSal() {
        return sal;
    }

    public void setSal(Double sal) {
        this.sal = sal;
    }
}

and its companion Department:

package nl.amis.ejb30.hrm;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="DEPT")
public class Department implements Serializable {
    private Long deptno;

    private String dname;

    private String location;

    public Department() {
    }

    @Id
    @Column(name="DEPTNO")
    public Long getDeptno() {
        return deptno;
    }

    public void setDeptno(Long deptno) {
        this.deptno = deptno;
    }

    @Column(name="DNAME")
    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    @Column(name="LOC")
    public String getLocation() {
        return location;
    }

    public void setLocation(String loc) {
        this.location = loc;
    }
}

 

Provide the persistence.xml file that links Entities (or Domain Classes or POJOs) to a database connection

The crucial element that links the annotated classes to a specific database in the case of out-of-container persistence is the persistence.xml. This file is read upon instantation of the EntityManagerFactory. It must be located in the classes/META-INF directory (or in one of the jar-files on the classpath).

In the persistence.xml file, a Persistence Unit is specified, a bundle of Entities that are mapped to the same Database Connection. For my example, persistence.xml maps to tables in my local Oracle database, more specifically the Scott schema in that database:

<persistence xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="pu1">
        <!-- Provider class name is required in Java SE -->
        <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
        <!-- All persistence classes must be listed -->
        <class>nl.amis.ejb30.hrm.Department</class>
        <class>nl.amis.ejb30.hrm.Employee</class>
        <properties>
            <!-- Provider-specific connection properties -->
            <property name="jdbc.driver" value="oracle.jdbc.driver.OracleDriver"/>
            <property name="jdbc.connection.string" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
            <property name="jdbc.user" value="scott"/>
            <property name="jdbc.password" value="tiger"/>
            <!-- Provider-specific settings -->
            <property name="toplink.logging.level" value="INFO"/>
        </properties>
    </persistence-unit>
</persistence>

The provider element set up in this file makes use of oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider. That is the default GlassFish reference implementation EntityManagerFactory implementation. Not an Oracle specific class. Allthough of course it is very similar to the EMF that Oracle ships as part of its Toplink commercial offering.

Create a Business Service class that provides Entitiy Services (like an out-of-container Session Bean)

In order to isolate our application from persistency details, we implement a Business Service that the application can talk to. This HrmService class provides a number of Department and Employee related services, that are internally implemented using an EJB 3.0 EntityManager. The HrmService class is very much like the SessionBean you would create for in-container deployment. It looks like this:

package nl.amis.ejb30.hrm;

import java.sql.Timestamp;

import java.util.List;
import java.util.ListIterator;

import javax.persistence.Query;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class HrmService {
    public HrmService() {

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu1");
    // create EntityManager
    EntityManager em = emf.createEntityManager();
    this.setEntityManager(em);
    }

    private EntityManager _entityManager;

    public EntityManager getEntityManager() {
    return _entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
    _entityManager = entityManager;
    }

    public List<Department> findAllDept() {
    return getEntityManager().createQuery("select object(o) from Department o").getResultList();
    }

    public Department createDept(Long deptno, String dname,
               String loc) {
    final Department dept = new Department();
    dept.setDeptno(deptno);
    dept.setDname(dname);
    dept.setLocation(loc);
    getEntityManager().persist(dept);
    return dept;
    }

    public List<Employee> findAllEmp()  {
    return getEntityManager().createQuery("select object(o) from Employee o").getResultList();
    }

    public Employee createEmp(Double comm, Long deptno, Long empno, String ename,
             Timestamp hiredate, String job, Long mgr,
             Double sal) {
    final Employee emp = new Employee();
    emp.setComm(comm);
    emp.setDeptno(deptno);
    emp.setEmpno(empno);
    emp.setEname(ename);
    emp.setHiredate(hiredate);
    emp.setJob(job);
    emp.setMgr(mgr);
    emp.setSal(sal);
    getEntityManager().persist(emp);
    return emp;
    }

    public void updateEntity(Object entity) {
    getEntityManager().merge( entity );
    }

    public void deleteEntity(Object entity) {
    final EntityManager em = getEntityManager();
    em.remove(em.merge(entity));
    }

    public void refreshEntity(Object entity) {
    getEntityManager().refresh( entity );
    }

    public void shutdown() {
    getEntityManager().close();
    }

}

 

Create a Client that leverages the Business Service to do stuff

To test or explore the functionality of our HrmService, we create a simple Service Consumer, a stand-alone J2SE application – in fact nothing more than a single MAIN method- that calls upon the Hrm Service to perform various services. The Client browses through all Employees, creates and persists a new Department object and queries that object afterwards.

package nl.amis.ejb30.hrm;

import java.util.List;

import javax.persistence.EntityTransaction;
import javax.persistence.Query;

public class HrmClient {
    public static void main(String[] args) {
       HrmService hrmService = new HrmService();

       // use the predefined service method for finding all employees
       // and printing all their names
       List<Employee>  emps = hrmService.findAllEmp();
       for (Employee e : emps) { System.out.println(e.getEname()); }

       // inside a transaction, create a new department - and persist it!
       EntityTransaction tx = hrmService.getEntityManager().getTransaction();
       tx.begin();
       // Create a new customer and persist it.
       hrmService.createDept(new Long(50),"New Dept", "Nieuwegein");
       // Commit the transaction
       tx.commit();

       // run an EJBQL query selecting a Department by some Search Criterium
       String ejbql = "SELECT d FROM Department d WHERE d.location = :location";  // you can tell this is an EJBQL query
       // because of the select d from department d - the whole record is selected, instead of individual columns
       // and it is selected from the Class-name Department , not the Table-name DEPT!
       Query query = hrmService.getEntityManager().createQuery(ejbql);
       query.setParameter("location", "Nieuwegein");
       List<Department> result = query.getResultList();
       // use a little Java 5.0 Autoboxing for simple, elegant for-looping
       for( Department d:result) {
           System.out.println("Department "+d.getDeptno()+"  "+d.getDname());
       }
       // Close this HrmService instance (and implicitly the EntityManager)
       hrmService.shutdown();
    }
}

Run Application

The application looks now as follows on the file system:

Getting Started with EJB 3.0 Persistence out-of-container using the Reference Implementation (GlassFish) ejb30 hrmappli

The following statement on the commandline suffices to run the nl.amis.ejb30.hrm.HrmClient class’s main method in a EJB 3.0 persistence context.

 java -javaagent:c:\glassfish/glassfish/lib/toplink-essentials-agent.jar  -classpath C:\glassfish\ejb30_se\se1\classes;C:\glassfish\glassfish\lib\javaee.jar;
C:\glassfish\glassfish\lib\toplink-essentials-agent.jar;C:\glassfish\glassfish\lib\toplink-essentials.jar;C:\jdev10_1_3_EA1\jdbc\lib\ojdbc14dms.jar;
C:\jdev10_1_3_EA1\jdbc\lib\orai18n.jar;C:\jdev10_1_3_EA1\jdbc\lib\ocrs12.jar;C:\jdev10_1_3_EA1\diagnostics\lib\ojdl.jar;
C:\jdev10_1_3_EA1\lib\dms.jar nl.amis.ejb30.hrm.HrmClient

This statement involes two sets of jar-files: first the jar-files javaee.jar, toplnk-essentials-agent.jar, toplink-essentials.jar – these are the libraries that are part of the GlassFish reference implementation of EJB 3.0 Persistence. The second bunch, ojdebc14dms.jar, orai18n.jar, ocrs12.jar, dms.jar and ojd1.jar, is used because of the Oracle JDBC Driver. These would be replaced when you use a different database driver.

Note: you need to have JDK or JRE 5.0 on the PATH. Note2: in this case, I make use of the OracleDriver to connect to the database, hence the references to Oracle specific JARs. When connecting to other databases using other drives all JARs in the C:\jdev10_1_3_EA1\jdbc\lib\ directory would be replaced. Note 3: Even though the use of toplink related JARs may sound Oracle specific, in this case they are the JARs from the Reference Implementation- open source and freely available. The outcome of this command looks like this:

Getting Started with EJB 3.0 Persistence out-of-container using the Reference Implementation (GlassFish) hrmClientOutput

Next Steps

Going from here there are a few things I would like to investigate.

First of all I want to look at more complex mappings and annotations. I want to see how relations are implemented, 1-to-many, many-to-one and many-to-many relations. I want to see how I can use ordinary collections with these relations. I want to get a feel for ‘lazy loading’.

Then there is the challenge of the synchronization between database and OO world in a multiuser-environment, possibly solved by the JVM level cache – something that is not part of GlassFish or the EJB 3.0 specification. Also the situation that I run into quite frequently where the database through default values and triggers can add to Insert and Update statements (in a very AOP like fashion come to think of it). In such a situation, persisting an object through the javax.persistence API and the EntityManager should be followed immediately by a flush and refresh to capture the changes applied by the database. I wonder whether I would have to address this in the Service POJO (the out of container equivalent of the Session Bean) or whether is possible to extend the EntityManager and – using my own annotations to specify the properties and entities that need refreshing after insert or update – automatically apply the logic in extended merge and persist methods.

I am curious to find out where the various implementations – Hibernate, Toplink and others – deviate from the RI; what they add to the specificaion. And how easy it is to introduce their extensions of the standard. And how difficult a migration between O/RM frameworks becomes if you program against the javax.persistence interfaces.

Conclusion and Sumnmary

It turns out to be very straightforward to get EJB 3.0 Persikstence to work, outside of a container. The concepts are – not surprisingly- pretty familiar: you create POJO Domain Objects, you specify how they map to table and columns and you make use of some sort of session or service or manager object to invoke persistency services. This is no different from frameworks like Hibernate or Toplink. From what I have seen already from the more advanced mapping facilities in EJB 3.0, I am pretty sure we can most of the persistency functionality we need. Not all of it, but then again, no framework provides all I need or desire.

It is important to note that if we were to use a different EJB 3.0 Persistence Implementation, for example Hibernate or Toplink instead of GlassFish RI, we would not have had to change any code. None of the classes, neither the domain classes nor the HrmService class, would be any different. The only change would be in the persistence.xml configuration file.

The importance of EJB 3.0 Persistence lies not so much in what it adds in ORM functionality: Hibernate, Toplink and other provide and have provided for years what is in EJB 3.0 Persistence. The real importance is in the fact that the same way of specifying the mapping and invoking the Persistence Services can be used both inside and outside EJB containers and across ORM frameworks! In the past I have used TopLink, Hibernate, a little iBatis, Spring JDBC and plain JDBC, Oracle Business Components for Java (ADF BC), Spring with TopLink and Spring with Hibernate and several others as well, like EJB CMP. While I can use this to distinguish myself among colleagues, it is really a pain to learn the intricacies of all these ORM technologies. It is similar yet different! Now that is not going away completely of course – since each implementation will add its own specific characteristisch on top of the EJB 3.0 Specification. Toplink will add its cache, special locking features and other extensions. So you will still need to learn such implementation specific details. But only when you need them and to a much lesser extent than before.

Porting applications – not just developers – between implementation should also become much simpler. As long as you stick to the EJB 3.0 standard set of annotations and services, the application should be perfectly portable between implementations. And of course, portable between in- and out-of container, if nothing else at least for unit-testing. And you can start developing an application for stand-alone, J2SE deployment, and decide later on to deploy inside a container, as proper EJBs in a distributed deployment setting.

Getting started with EJB 3.0 is not hard, does not take long and is actually good fun!

Resources

Download the Source Code shown in this article here: src.zip

The next installment on EJB 3.0 – Starting with Relations: Diving deeper into EJB 3.0 Persistence with GlassFish (RI) – still out of container

 

And the third episode one Many To Many relations: EJB 3.0 Persistence – ManyToMany relations or the objectification of the intersection table

The fourth article in the series on EJB 3.0 Persistence, more specifically the @Version annotation.

EJB 3.0 Persistence – using the @Version annotation for Optimistic Locking – in the GlassFish Reference Implementation

GlassFish Project – Entity Persistence Support

Java Community Process – homepage for JSR-220 EJB 3.0

EJB 3.0 Resources on the Oracle Technology Network

JBoss EJB 3.0

Introduction to using Java Persistence API in a web application in Java EE environment on Sahoo’s Blog (posted 4th December 2005)

Dali – Eclipse plugin : The goal of the Dali EJB Object-Relational Mapping project is to build extensible frameworks and exemplary tools for the definition and editing of Object-Relational (O/R) mappings for EJB 3.0 Entity Beans.

Mike Keiths Weblog: System.out

Loosely Coupled Blog by Raghu Kodali

Ease of Development in Enterprise JavaBeans Technology, By Ed Ort, October 2004 – Sun Developer Network (SDN)

POJO Application Frameworks: Spring Vs. EJB 3.0, by Michael Juntao Yuan, 06/29/2005 OnJava.com

The EJB 3.0 Hibernate Fallacy – shocking and revealing blog-post with comment-thread including strong language by Gavin King

Out-of-Container testing harness – by Debu Panda on Oracle Technology Framework

 

 

 

16 Comments

  1. tibet tour February 24, 2009
  2. Brad Matlack April 24, 2007
  3. Brad Matlack April 24, 2007
  4. Wouter van Reeven January 20, 2006
  5. Steven Peh January 11, 2006
  6. Lucas Jellema January 7, 2006
  7. Lucas Jellema January 3, 2006
  8. Lucas Jellema January 2, 2006
  9. Lucas Jellema January 1, 2006
  10. Lucas Jellema December 31, 2005
  11. Mitesh December 29, 2005
  12. Jasper December 29, 2005
  13. Ryan December 28, 2005
  14. Debu Pand December 28, 2005
  15. Lucas Jellema December 28, 2005