Starting with EJB 3.0 Persistence in Eclipse 3.1 using the GlassFish Reference Implementation

This article describes a quick route to starting out with the relatively new EJB 3.0 Persistence API from the Eclipse 3.1 IDE. In a subsequent article, I will discuss the Dali EJB 3.0 Persistence plugin for Eclipse, developed by Oracle, BEA (after taking over SolarMetric) and JBoss.

A lot has been said about EJB 3.0 Persistence. I will not add much to those discussions. It seems to me that the EJB 3.0 Persistence API will prove extremely popular with applications, both running with EJBs in J2EE Containers as well as with Web Applications and even Java Client and stand-alone applications running outside the EJB Container. All major ORM frameworks – including Toplink, Hibernate and Kodo – provide implementations of EJB 3.0 Persistence. So it seems logical that most applications will do their ORM activities through the EJB 3.0 Persistence API, regardless of the implementation they will choose.

In a previous article I have demonstrated how to set up a JDeveloper 10.1.3 environment with the GlassFish reference implementation (actually Toplink Essentials) of EJB 3.0 Persistence:Using GlassFish Reference Implementation of EJB 3.0 Persistence with JDeveloper 10.1.3EA. Before that I discussed getting started with EJB 3.0 Persistence using GlassFish in general:Getting Started with EJB 3.0 Persistence out-of-container using the Reference Implementation (GlassFish). The article you are reading now adds to these two article in discussing the specific set up of Eclipse. Which by the way is very simple.

Let’s assume for the moment that you already have Eclipse 3.1 set up against a JDK 5.0. The steps from here are:

  • Download and install GlassFish (well, mainly download and extract)
  • Start up Eclipse and create a new Java Project
  • Configure the projec
  • Create the Entities, the persistence.xml and the application
  • Run the application, using the proper JVM options

It is not difficult!

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 1 February 2006 (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.

Start up Eclipse and create a new Java Project

Create a new Eclipse Java Project.

Starting with EJB 3.0 Persistence in Eclipse 3.1 using the GlassFish Reference Implementation eclipseEjb30NewJavaApp

Configure the project

 

Set the JDK Compliance to (Java) 5.0:
Starting with EJB 3.0 Persistence in Eclipse 3.1 using the GlassFish Reference Implementation eclipseEjb30Compl
Add the following libraries:

Starting with EJB 3.0 Persistence in Eclipse 3.1 using the GlassFish Reference Implementation eclipseEjb30Libs
javaee.jar, toplink-essentials.jar (from the GlassFish_Home\lib directory) and dms.jar, ocrs12.jar and ojdbc14.jar (from an Oracle Client or JDBC installation).
The first three jars are taken from the GlassFish installation and represent the Reference Implementation for the EJB 3.0 javax.persistence interfaces. The other jars are only required when using the Oracle JDBC driver – they can be replaced with similar jars for any other JDBC driver.

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 one extremely simple POJO, that I decorate with EJB 3.0
annotations to instruct the EJB 3.0 EntityManager about mapping it to
a database table. Note: this is really the simplest set of annotations,
just to get the show going. I use the AMIS Library System, quite a popular case for workshops in my company.

package nl.amis.ejb30.als;

import java.io.Serializable;

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

@Entity
@Table(name="ALS_PUBLISHERS")
public class Publisher implements Serializable {

    private Long id;

    private String locationInCountry;

    private byte[] logo;

    private String name;

    private String website;

    public Publisher() {
    }

    public Publisher(Long id) {
        this.id = id;
    }

    @Id
    @Column(name="ID",  nullable=false)
    public Long getId() {
        return id;
    }

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

    @Column(name="LOCATION_IN_COUNTRY")
    public String getLocationInCountry() {
        return locationInCountry;
    }

    public void setLocationInCountry(String locationInCountry) {
        this.locationInCountry = locationInCountry;
    }

    @Column(name="LOGO")
    public byte[] getLogo() {
        return logo;
    }

    public void setLogo(byte[] logo) {
        this.logo = logo;
    }

    @Column(name="NAME")
    public String getName() {
        return name;
    }

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

    @Column(name="WEBSITE")
    public String getWebsite() {
        return website;
    }

    public void setWebsite(String website) {
        this.website = website;
    }

}

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 ALS schema in that database:

<persistence xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="AlsPU">
        <!-- 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.als.Publisher</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="als"/>
            <property name="jdbc.password" value="als"/>
            <!-- Provider-specific settings -->
            <property name="toplink.logging.level" value="FINE"/>
        </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 Entity 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 LibraryService
class provides a number of Publisher related services,
that are internally implemented using an EJB 3.0 EntityManager. The LibraryService class is very much like the SessionBean you would create for
in-container deployment. It looks like this:

package nl.amis.ejb30.als;

import java.util.List;

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


public class LibraryService {
    public LibraryService() {
        EntityManagerFactory emf = 
            Persistence.createEntityManagerFactory("AlsPU");
        // create EntityManager
        EntityManager em = emf.createEntityManager();
        // note: alternative is EXTENDED EntityManager ; an EXTENDED EntityManager keeps objects attached
        // EntityManager em = emf.createEntityManager(PersistenceContextType.EXTENDED);
        this.setEntityManager(em);
    }

    private EntityManager _entityManager;

    public EntityManager getEntityManager() {
        return _entityManager;
    }

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

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

    public Publisher findPublisher(Long id) {
        return getEntityManager().find(Publisher.class, id);
    }

    
    public void savePublisher(Publisher publisher) {
        EntityTransaction tx = getEntityManager().getTransaction();
        tx.begin();
        getEntityManager().persist(publisher);        
        // Commit the transaction
        tx.commit();
    }

    public Publisher createPublisher(Long id, String name) {
        Publisher p = new Publisher();
        p.setId(id);
        p.setName(name);
        savePublisher(p);        
        return p;
    }
    
    public void removePublisher(Publisher p) {
      // note: we cannot simply pass in any object to be removed:
      // the object may not be "detached"; any object that was created or
      // retrieved by the EntityManager in the current transaction is not
      // detached; any object from outside the current transaction however is
      // considered detached. We "attach"  a detached object by "merging" it:
      // note2: if we were using an EXTENDED EntityManager, any object that
      // was retrieved at any time by the EntityManager - across Transaction
      // boundaries - would still be "attached"
      
      // create an attached Publisher object for the (possibly) detached p
      Publisher p2 = getEntityManager().merge(p);
      // remove the attached counterpart of the (potentially) detached p
      getEntityManager().remove(p2);
    }
    
    private EntityTransaction transaction;
    
    public void startTransaction() {
      transaction = getEntityManager().getTransaction();
      transaction.begin();        
    }
    
    public void endTransaction() {
       transaction.commit(); 
    }
}

Create a Client that leverages the Business Service to do stuff

To
test or explore the functionality of our LibraryService, we create a simple
Service Consumer, a stand-alone J2SE application – in fact nothing more
than a single MAIN method- that calls upon the LibraryService to perform
various services. The Client browses through all Publishers creates and
persists a new one and then removes it again

package nl.amis.ejb30.als;

import java.util.Collection;

public class LibraryClient {
    public LibraryClient() {
    }

    public static void main(String[] args) {
        LibraryService library = new LibraryService();
        System.out.println("List all Publishers:");
        Collection<Publisher> publishers = library.findAllPublishers();
        for (Publisher publisher : publishers) { 
          System.out.println(publisher.getName());
        }        
        Publisher publisher = new Publisher();
        publisher.setId(new Long(44));
        publisher.setName("My Publishing House");
        library.savePublisher(publisher);
        // alternative: create (and persist) new publisher
        Publisher p = library.createPublisher(new Long(45), "Great Books Inc.");
        
               // now remove the new Publishers
        library.startTransaction();
        library.removePublisher(publisher);
        library.removePublisher(library.findPublisher(new Long(45)));
        library.endTransaction();        
    }
}

Run Application

The application looks now as follows on the file system

 

In order to run the EJB 3.0 Persistence application correctly, we have to create some run-options.
When we have created an application – a class with a main – like LibraryClient, we can set these options. From the context menu on the project, select Run As and Run. Now we can set the options for when running the project.

Starting with EJB 3.0 Persistence in Eclipse 3.1 using the GlassFish Reference Implementation eclipseEjb30Run

Go to the Arguments tab in the Run wizard and set the correct VM arguments: -javaagent:GLASSFISH_HOME/lib/toplink-essentials-agent.jar.
Starting with EJB 3.0 Persistence in Eclipse 3.1 using the GlassFish Reference Implementation eclipseEjb30JVM
The Library application could look something like this:

Starting with EJB 3.0 Persistence in Eclipse 3.1 using the GlassFish Reference Implementation eclipseEjb30Proj
Note: the table ALS_PUBLISHERS that is used in this example looks as follows:

CREATE TABLE "ALS_PUBLISHERS"
(
"ID" NUMBER (10,0) NOT NULL,
"WEBSITE" VARCHAR2 (50),
"NAME" VARCHAR2 (50),
"COUNTRY" VARCHAR2 (2),
"LOCATION_IN_COUNTRY" VARCHAR2 (50),
"LOGO" BLOB,
  CONSTRAINT PBR_PK
  PRIMARY KEY ( ID ) 
)
;

You can insert some demo data from this script:

INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,
LOCATION_IN_COUNTRY ) VALUES ( 
2, 'www.wrox.com', 'Wrox (label of Wiley Publishing)', 'us', 'Indianapolis (IN)'); 
INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,
LOCATION_IN_COUNTRY ) VALUES ( 
3, 'www.wiley.com', 'Wiley and Sons', 'us', 'New York (NY)'); 
INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,
LOCATION_IN_COUNTRY ) VALUES ( 
1, 'www.oreilly.com', 'O''Reilly', 'us', 'Sebastopol, CA'); 
INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,
LOCATION_IN_COUNTRY ) VALUES ( 
4, 'http://www.manning.com', 'Manning Publications Co.', 'us', NULL); 
INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,
LOCATION_IN_COUNTRY ) VALUES ( 
5, 'http://java.sun.com', 'SUN Microsystems', 'us', NULL); 
INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,
LOCATION_IN_COUNTRY ) VALUES ( 
6, 'http://books.mcgraw-hill.com', 'McGrawHill', 'us', NULL); 
commit;

10 Comments

  1. Cam Bazz November 21, 2007
  2. sud January 31, 2007
  3. Marina April 16, 2006
  4. Gunnar Wagenknecht April 15, 2006
  5. Gunnar Wagenknecht April 15, 2006
  6. Lucas Jellema March 26, 2006
  7. mlopez March 26, 2006
  8. Denis Robert February 20, 2006
  9. Lucas Jellema February 20, 2006
  10. Sudhakar February 19, 2006