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

10

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.

Configure the project 

 

Set the JDK Compliance to (Java) 5.0:
 
Add the following libraries:


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;<br /><br />import java.io.Serializable;<br /><br />import javax.persistence.Column;<br />import javax.persistence.Entity;<br />import javax.persistence.Id;<br />import javax.persistence.Table;<br /><br />@Entity<br />@Table(name=&quot;ALS_PUBLISHERS&quot;)<br />public class Publisher implements Serializable {<br /><br />    private Long id;<br /><br />    private String locationInCountry;<br /><br />    private byte[] logo;<br /><br />    private String name;<br /><br />    private String website;<br /><br />    public Publisher() {<br />    }<br /><br />    public Publisher(Long id) {<br />        this.id = id;<br />    }<br /><br />    @Id<br />    @Column(name=&quot;ID&quot;,  nullable=false)<br />    public Long getId() {<br />        return id;<br />    }<br /><br />    public void setId(Long id) {<br />        this.id = id;<br />    }<br /><br />    @Column(name=&quot;LOCATION_IN_COUNTRY&quot;)<br />    public String getLocationInCountry() {<br />        return locationInCountry;<br />    }<br /><br />    public void setLocationInCountry(String locationInCountry) {<br />        this.locationInCountry = locationInCountry;<br />    }<br /><br />    @Column(name=&quot;LOGO&quot;)<br />    public byte[] getLogo() {<br />        return logo;<br />    }<br /><br />    public void setLogo(byte[] logo) {<br />        this.logo = logo;<br />    }<br /><br />    @Column(name=&quot;NAME&quot;)<br />    public String getName() {<br />        return name;<br />    }<br /><br />    public void setName(String name) {<br />        this.name = name;<br />    }<br /><br />    @Column(name=&quot;WEBSITE&quot;)<br />    public String getWebsite() {<br />        return website;<br />    }<br /><br />    public void setWebsite(String website) {<br />        this.website = website;<br />    }<br /><br />}<br />&nbsp;

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:

&lt;persistence xmlns=&quot;http://java.sun.com/xml/ns/persistence&quot;&gt;<br />    &lt;persistence-unit name=&quot;AlsPU&quot;&gt;<br />        &lt;!-- Provider class name is required in Java SE --&gt;<br />        &lt;provider&gt;oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider&lt;/provider&gt;<br />        &lt;!-- All persistence classes must be listed --&gt;<br />        &lt;class&gt;nl.amis.ejb30.als.Publisher&lt;/class&gt;<br />        &lt;properties&gt;<br />            &lt;!-- Provider-specific connection properties --&gt;<br />            &lt;property name=&quot;jdbc.driver&quot; value=&quot;oracle.jdbc.driver.OracleDriver&quot;/&gt;<br />            &lt;property name=&quot;jdbc.connection.string&quot; value=&quot;jdbc:oracle:thin:@localhost:1521:ORCL&quot;/&gt;<br />            &lt;property name=&quot;jdbc.user&quot; value=&quot;als&quot;/&gt;<br />            &lt;property name=&quot;jdbc.password&quot; value=&quot;als&quot;/&gt;<br />            &lt;!-- Provider-specific settings --&gt;<br />            &lt;property name=&quot;toplink.logging.level&quot; value=&quot;FINE&quot;/&gt;<br />        &lt;/properties&gt;<br />    &lt;/persistence-unit&gt;<br />&lt;/persistence&gt;  <br />

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;<br /><br />import java.util.List;<br /><br />import javax.persistence.EntityManager;<br />import javax.persistence.EntityManagerFactory;<br />import javax.persistence.EntityTransaction;<br />import javax.persistence.Persistence;<br />import javax.persistence.PersistenceContextType;<br /><br /><br />public class LibraryService {<br />    public LibraryService() {<br />        EntityManagerFactory emf = <br />            Persistence.createEntityManagerFactory(&quot;AlsPU&quot;);<br />        // create EntityManager<br />        EntityManager em = emf.createEntityManager();<br />        // note: alternative is EXTENDED EntityManager ; an EXTENDED EntityManager keeps objects attached<br />        // EntityManager em = emf.createEntityManager(PersistenceContextType.EXTENDED);<br />        this.setEntityManager(em);<br />    }<br /><br />    private EntityManager _entityManager;<br /><br />    public EntityManager getEntityManager() {<br />        return _entityManager;<br />    }<br /><br />    public void setEntityManager(EntityManager entityManager) {<br />        _entityManager = entityManager;<br />    }<br /><br />    public List&lt;Publisher&gt; findAllPublishers() {<br />        return getEntityManager().createQuery(&quot;select object(o) from Publisher o&quot;).getResultList();<br />    }<br /><br />    public Publisher findPublisher(Long id) {<br />        return getEntityManager().find(Publisher.class, id);<br />    }<br /><br />    <br />    public void savePublisher(Publisher publisher) {<br />        EntityTransaction tx = getEntityManager().getTransaction();<br />        tx.begin();<br />        getEntityManager().persist(publisher);        <br />        // Commit the transaction<br />        tx.commit();<br />    }<br /><br />    public Publisher createPublisher(Long id, String name) {<br />        Publisher p = new Publisher();<br />        p.setId(id);<br />        p.setName(name);<br />        savePublisher(p);        <br />        return p;<br />    }<br />    <br />    public void removePublisher(Publisher p) {<br />      // note: we cannot simply pass in any object to be removed:<br />      // the object may not be &quot;detached&quot;; any object that was created or<br />      // retrieved by the EntityManager in the current transaction is not<br />      // detached; any object from outside the current transaction however is<br />      // considered detached. We &quot;attach&quot;  a detached object by &quot;merging&quot; it:<br />      // note2: if we were using an EXTENDED EntityManager, any object that<br />      // was retrieved at any time by the EntityManager - across Transaction<br />      // boundaries - would still be &quot;attached&quot;<br />      <br />      // create an attached Publisher object for the (possibly) detached p<br />      Publisher p2 = getEntityManager().merge(p);<br />      // remove the attached counterpart of the (potentially) detached p<br />      getEntityManager().remove(p2);<br />    }<br />    <br />    private EntityTransaction transaction;<br />    <br />    public void startTransaction() {<br />      transaction = getEntityManager().getTransaction();<br />      transaction.begin();        <br />    }<br />    <br />    public void endTransaction() {<br />       transaction.commit(); <br />    }<br />}<br />

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;<br /><br />import java.util.Collection;<br /><br />public class LibraryClient {<br />    public LibraryClient() {<br />    }<br /><br />    public static void main(String[] args) {<br />        LibraryService library = new LibraryService();<br />        System.out.println(&quot;List all Publishers:&quot;);<br />        Collection&lt;Publisher&gt; publishers = library.findAllPublishers();<br />        for (Publisher publisher : publishers) { <br />          System.out.println(publisher.getName());<br />        }        <br />        Publisher publisher = new Publisher();<br />        publisher.setId(new Long(44));<br />        publisher.setName(&quot;My Publishing House&quot;);<br />        library.savePublisher(publisher);<br />        // alternative: create (and persist) new publisher<br />        Publisher p = library.createPublisher(new Long(45), &quot;Great Books Inc.&quot;);<br />        <br />               // now remove the new Publishers<br />        library.startTransaction();<br />        library.removePublisher(publisher);<br />        library.removePublisher(library.findPublisher(new Long(45)));<br />        library.endTransaction();        <br />    }<br />}<br /> 

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.


 
Go to the Arguments tab in the Run wizard and set the correct VM arguments: -javaagent:GLASSFISH_HOME/lib/toplink-essentials-agent.jar.
 
The Library application could look something like this:
 
 
 

Note: the table ALS_PUBLISHERS that is used in this example looks as follows:

CREATE TABLE &quot;ALS_PUBLISHERS&quot;<br />(<br />&quot;ID&quot; NUMBER (10,0) NOT NULL,<br />&quot;WEBSITE&quot; VARCHAR2 (50),<br />&quot;NAME&quot; VARCHAR2 (50),<br />&quot;COUNTRY&quot; VARCHAR2 (2),<br />&quot;LOCATION_IN_COUNTRY&quot; VARCHAR2 (50),<br />&quot;LOGO&quot; BLOB,<br />  CONSTRAINT PBR_PK<br />  PRIMARY KEY ( ID ) <br />)<br />;<br />&nbsp;<br /><p>You can insert some demo data from this script:</p>
INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,<br />LOCATION_IN_COUNTRY ) VALUES ( <br />2, 'www.wrox.com', 'Wrox (label of Wiley Publishing)', 'us', 'Indianapolis (IN)'); <br />INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,<br />LOCATION_IN_COUNTRY ) VALUES ( <br />3, 'www.wiley.com', 'Wiley and Sons', 'us', 'New York (NY)'); <br />INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,<br />LOCATION_IN_COUNTRY ) VALUES ( <br />1, 'www.oreilly.com', 'O''Reilly', 'us', 'Sebastopol, CA'); <br />INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,<br />LOCATION_IN_COUNTRY ) VALUES ( <br />4, 'http://www.manning.com', 'Manning Publications Co.', 'us', NULL); <br />INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,<br />LOCATION_IN_COUNTRY ) VALUES ( <br />5, 'http://java.sun.com', 'SUN Microsystems', 'us', NULL); <br />INSERT INTO ALS_PUBLISHERS ( ID, WEBSITE, NAME, COUNTRY,<br />LOCATION_IN_COUNTRY ) VALUES ( <br />6, 'http://books.mcgraw-hill.com', 'McGrawHill', 'us', NULL); <br />commit;<br />&nbsp;
Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

10 Comments

  1. Hello;

    Upon running the LibraryClient I get:

    [TopLink Config]: 2007.11.21 01:13:06.554–ServerSession(32233307)–Thread(Thread[main,5,main])–The alias name for the entity class [class nl.amis.ejb30.als.Publisher] is being defaulted to: Publisher.
    [TopLink Info]: 2007.11.21 01:13:06.621–ServerSession(32233307)–Thread(Thread[main,5,main])–TopLink, version: Oracle TopLink Essentials – 2.0 (Build b58g-fcs (09/07/2007))
    Exception in thread “main” java.lang.NullPointerException
    at sun.jdbc.odbc.JdbcOdbcDriver.initialize(JdbcOdbcDriver.java:436)
    at sun.jdbc.odbc.JdbcOdbcDriver.connect(JdbcOdbcDriver.java:153)
    at java.sql.DriverManager.getConnection(DriverManager.java:582)
    at java.sql.DriverManager.getConnection(DriverManager.java:154)
    at oracle.toplink.essentials.sessions.DefaultConnector.connect(DefaultConnector.java:100)
    at oracle.toplink.essentials.sessions.DatasourceLogin.connectToDatasource(DatasourceLogin.java:184)
    at oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl.loginAndDetectDatasource(DatabaseSessionImpl.java:582)
    at oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:280)
    at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:229)
    at oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerFactoryImpl.getServerSession(EntityManagerFactoryImpl.java:93)
    at oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerFactoryImpl.createEntityManagerImpl(EntityManagerFactoryImpl.java:126)
    at oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerFactoryImpl.createEntityManagerImpl(EntityManagerFactoryImpl.java:120)
    at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:91)
    at nl.amis.ejb30.als.LibraryService.(LibraryService.java:17)
    at nl.amis.ejb30.als.LibraryClient.main(LibraryClient.java:10)

  2. I am attempting a JPA application in Java SE using Eclipse 3.2.1 and Maven2. My project structure in eclipse is:

    |______src/main/java
    |_________mypackage
    |___________MyClass.java
    |______META-INF
    |_________persistence.xml

    When I run my MyClass.java as a java application I get:

    Exception in thread “main” javax.persistence.PersistenceException: No Persistence provider for EntityManager named WorkOrder
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:89)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:60)
    at mypackage.MyClass.init(MyClass.java:26)
    at mypackage.MyClass.main(MyClass.java:16)

    I’ve tried placing the persistence.xml is various different folders but with no luck. I was wondering if anyone would know how to fix this.
    Thanks
    -sud

  3. I have one note and one question:
    A note. The properties names for GlassFish persistence implementation has been changed (see http://forums.java.net/jive/thread.jspa?threadID=13989&tstart=0 for details), and version element must be specified in presistence.xml as it’s required by the schema.

    A question: When did you last time tried to download the zip files with examples on GlassFish persistence example page? This links were broken for a short while in December, but has been fixed ever since.

    thanks,
    -marina

  4. It seems that it can find the persistence.xml file, but the file does not have an entry for the AlsPU provider.
    In the code, we have this line:

    Persistence.createEntityManagerFactory(“AlsPU”);

    This refers to an entry in the persistence.xml file:

    The name attribute of the persistence-unit element must match exactly with the string passed to the createEntityManagerFactory method.

    It would seem that your persistence.xml file is there, but it does not contain the proper persistence-unit element.

    good luck!

    Lucas

  5. i cant make it work, when i try to execute it a have this error:

    Exception in thread “main” javax.persistence.PersistenceException: No Persistence provider for EntityManager named AlsPU
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:89)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:60)
    at nl.amis.ejb30.als.LibraryService.(LibraryService.java:16)
    at nl.amis.ejb30.als.LibraryClient.main(LibraryClient.java:11)

    Its like the project cant recognize the persistece.xml file, but i know it is in the classpath

  6. Yes, you may want to download Glassfish’s Eclipse WTP plugin.

    If you build your initial WebApp under a different app server, (like Tomcat), you’ll have to change the project runtime. To do that, follow the steps below:

    1. Right-click on the project, then select properties.
    2. Select “Project Facets”, and click “Add/Remove Project Facets” in the bottom right corner
    3. Check the Sun DD Files facet, select “Show Runtimes”, and select a GlassFish runtime from the list.

    Now, instead of seeing the Tomcat runtime library in the Package Explorer, you’ll see
    Glassfish’s library, which will contain the core Java EE5 libraries, including all the
    annotations.

  7. I’m running Eclipse 3.1.2 on JDK 1.5.06. My project is set to JDK 5.0 compliance.

    But none of the EJB 3.0 annotations are being recognized and I get compile time errors
    saying cannot resolve type.

    Is there a plugin that I must install?