Tuesday, August 30, 2011

Mapping XMLTYPE

A recent posting on the TopLink Forum made it clear that previous descriptions of how to map a JPA entity attribute of type org.w3c.dom.Document to an Oracle database XMLTYPE column didn't provide enough information for schema (DDL) generation. So I thought I'd post an example that does. In the current 2.3.0 release of EclipseLink a mapping to XMLTYPE must be configured programatically through a descriptor customizer but declarative annotation and XML configuration support is scheduled for an upcoming release.

Here's a simple entity with an XML document 'resume' attribute:

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

import org.eclipse.persistence.annotations.Customizer;
import org.w3c.dom.Document;

@Entity
@Customizer(XMLTypeAttributeCustomizer.class)
public class JobCandidate {
	@Id
	@GeneratedValue
	private long id;

	private Document resume;

	public long getId() {
		return id;
	}

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

	public Document getMessage() {
		return resume;
	}

	public void setMessage(Document message) {
		this.resume = message;
	}
}


And here is a customizer class that will redefine the mapping for the resume attribute to be an EclipseLink DirectToXMLTypeMapping:

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.mappings.xdb.DirectToXMLTypeMapping;

public class XMLTypeAttributeCustomizer implements DescriptorCustomizer {
	@Override
	public void customize(final ClassDescriptor descriptor) throws Exception {
		// Remove JPA default Basic mapping
		descriptor.removeMappingForAttributeName("resume");
		final DirectToXMLTypeMapping mapping = new DirectToXMLTypeMapping();
		mapping.setAttributeName("resume");
		mapping.setFieldName("RESUME");
		mapping.getField().setColumnDefinition("sys.XMLTYPE");
		descriptor.addMapping(mapping);
	}
}

The hightlighted line sets the database type of the mapping which is used by EclipseLink DDL generation and produces:
[EL Fine]: CREATE TABLE JOBCANDIDATE (ID NUMBER(19) NOT NULL, RESUME sys.XMLTYPE, PRIMARY KEY (ID))

Thursday, July 28, 2011

GlassFish 3.1.1 Ships with EclipseLink 2.3!

GlassFish 3.1.1 shipped with EclipseLink 2.3 today! Check out my post on the EclipseLink Team Blog for details.

Shaun Smith, EclipseLink Team
@shaunmsmith

Tuesday, March 1, 2011

The Poetry of Google Translated Twitter

Doug Clarke recently setup a Google Reader aggregator that included a search of Twitter for the term "JPA". Some of the messages are not in English but Google Reader is happy to translate them. Unfortunately, or maybe fortunately in this case, mechanical translation isn't perfect and sometimes results in what feels like free verse poetry.

Here's a sample:
  • JPA is the itches of God
  • I eat, jpa back. a kiss that cheese is my: *
  • In JPA I do not feel hungry. I have to live there.

Monday, July 5, 2010

EclipseLink JPA Native Constructor Queries

I saw a posting over on JavaRanch from someone wanting to return JavaBean results from a Native SQL query in JPA. Turns out this is pretty easily done with EclipseLink and I just happen to have already written a utility class for just this purpose so I thought I'd post it.

You can configure a native SQL query to return POJOs in a single line of code using the JavaBeanResult utility class:
   JavaBeanResult.setQueryResultClass(query, SomeBeanClass.class); 
This will install an EclipseLink query redirector which will intercept the SQL query results and build JavaBean objects from them.

The JavaBeanResult utility class will do the job but it relies on the developer to align the field selected in their SQL query with the constructor arguments for their bean. For example, for the code below to work, SupplierBean must have a constructor with three arguments with types compatible with the SQL result types for columns id, name, and version. JavaBeanResult uses the EclipseLink ConversionManager to convert each SQL result value to the declared type of the corresponding constructor argument. If it can't you'll get an exception.

       Query query = getEntityManager()  
.createNativeQuery("SELECT ID, NAME, VER FROM SUPPLIER");
JavaBeanResult.setQueryResultClass(query, SupplierBean.class);
List<SupplierBean> results = query.getResultList();
    public SupplierBean(int id, String name, Long version) {  

--Shaun


Complete source for JavaBeanResult.java:
 /*******************************************************************************  
* Copyright (c) 2010 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* @author shsmith
******************************************************************************/
package org.eclipse.persistence.example.jpa.nativesql.constructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Query;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.QueryRedirector;
import org.eclipse.persistence.sessions.Record;
import org.eclipse.persistence.sessions.Session;
/***
* This class is a simple query redirector that intercepts the result of a
* native query and builds an instance of the specified JavaBean class from each
* result row. The order of the selected columns musts match the JavaBean class
* constructor arguments order.
*
* To configure a JavaBeanResult on a native SQL query use:
* JavaBeanResult.setQueryResultClass(query, SomeBeanClass.class);
* where query is either a JPA SQL Query or native EclipseLink DatabaseQuery.
*
* @author shsmith
*
*/
public final class JavaBeanResult implements QueryRedirector {
private static final long serialVersionUID = 3025874987115503731L;
protected Class<?> resultClass;
public static void setQueryResultClass(Query query, Class<?> resultClass) {
JavaBeanResult javaBeanResult = new JavaBeanResult(resultClass);
DatabaseQuery databaseQuery = JpaHelper.getDatabaseQuery(query);
databaseQuery.setRedirector(javaBeanResult);
}
public static void setQueryResultClass(DatabaseQuery query,
Class<?> resultClass) {
JavaBeanResult javaBeanResult = new JavaBeanResult(resultClass);
query.setRedirector(javaBeanResult);
}
protected JavaBeanResult(Class<?> resultClass) {
this.resultClass = resultClass;
}
@SuppressWarnings("unchecked")
public Object invokeQuery(DatabaseQuery query, Record arguments,
Session session) {
List<Object> results = new ArrayList<Object>();
try {
Constructor<?> javaBeanClassConstructor =
(Constructor<?>) resultClass.getDeclaredConstructors()[0];
Class<?>[] constructorParameterTypes =
javaBeanClassConstructor.getParameterTypes();
List<Object[]> rows = (List<Object[]>) query.execute(
(AbstractSession) session, (AbstractRecord) arguments);
for (Object[] columns : rows) {
Object[] constructorArgs = new Object[constructorParameterTypes.length];
if (columns.length != constructorParameterTypes.length) {
throw new ColumnParameterNumberMismatchException(
resultClass);
}
for (int j = 0; j < columns.length; j++) {
Object columnValue = columns[j];
Class<?> parameterType = constructorParameterTypes[j];
// convert the column value to the correct type--if possible
constructorArgs[j] = ConversionManager.getDefaultManager()
.convertObject(columnValue, parameterType);
}
results.add(javaBeanClassConstructor.newInstance(constructorArgs));
}
} catch (ConversionException e) {
throw new ColumnParameterMismatchException(e);
} catch (IllegalArgumentException e) {
throw new ColumnParameterMismatchException(e);
} catch (InstantiationException e) {
throw new ColumnParameterMismatchException(e);
} catch (IllegalAccessException e) {
throw new ColumnParameterMismatchException(e);
} catch (InvocationTargetException e) {
throw new ColumnParameterMismatchException(e);
}
return results;
}
public final class ColumnParameterMismatchException extends
RuntimeException {
private static final long serialVersionUID = 4752000720859502868L;
public ColumnParameterMismatchException(Throwable t) {
super(
"Exception while processing query results-ensure column order matches constructor parameter order",
t);
}
}
public final class ColumnParameterNumberMismatchException extends
RuntimeException {
private static final long serialVersionUID = 1776794744797667755L;
public ColumnParameterNumberMismatchException(Class<?> clazz) {
super(
"Number of selected columns does not match number of constructor arguments for: "
+ clazz.getName());
}
}
}

Friday, April 16, 2010

CheckCacheThenDatabase in TopLink Grid

EclipseLink JPA has a number of useful query hints that allow you to query the cache rather than, or before, the database. This is useful because if you know you've warmed up your cache you can execute queries for objects and yet not have to query the database.

As of TopLink 11gR1 (11.1.1.2.0) TopLink Grid doesn't support these hints but it's only a problem for the 'Grid Cache' configuration in which Coherence is used as a shared cache replacement. In Grid Cache, only primary key queries are sent to Coherence and all other queries go to the database. In the 'Grid Read' and 'Grid Entity' configurations all read queries are directed to Coherence.

I was recently asked how to get a query to CheckCacheThenDatabase with Grid Cache and came up with the GridCacheQueryHelper utility class below. It provides the static method checkCacheThenDatabase which will adjust an EclipseLink JPA query so that it queries Coherence (if the query can be translated to a Filter) and if no results are found in Coherence then it queries the database.

Configuring a query would look something like:

        Query query = em.createQuery("select e from Employee e");
        GridCacheQueryHelper.checkCacheThenDatabase(query);
        List<Employee> employees = query.getResultList();


NOTE: this code is based on how TopLink Grid 11.1.1.2.0 is implemented and may not work with future releases.

--Shaun

/**
 * Utility class that provides implementations of EclipseLink 
 * query hints for use with TopLink Grid 'Grid Cache' Configuration.
 * 
 * 
@author shsmith
 *
 */

class GridCacheQueryHelper {
    
    
public static void checkCacheThenDatabase(Query jpqlQuery) {
        JpaHelper
            .getDatabaseQuery(jpqlQuery)
            .setRedirector(
new CoherenceCheckCacheThenDatabase());
    }
    
    
private static class CoherenceCheckCacheThenDatabase implements QueryRedirector {
        
        
public Object invokeQuery(DatabaseQuery query, Record arguments,
                Session session) {
            
// Create appropriate Coherence Query Redirector
            QueryRedirector redirector = null;
            
if (query.isReadObjectQuery()) {
                redirector = 
new ReadObjectFromCoherence();
            } 
else if (query.isReadAllQuery()) {
                redirector = 
new ReadAllFromCoherence();                
            } 
else { 
                
throw new RuntimeException("CheckCacheThenDatabase only supported on ReadObject and ReadAll Queries.");
            }
            
// If nothing returned from Coherence then query database.
            // But only if the query was only run against Coherence 
            // and no results were found.
            List results = (List)redirector.invokeQuery(query, arguments, session);
            
if ((results.isEmpty()) && (query.getDoNotRedirect())) {
                
return query.execute((AbstractSession)session, (AbstractRecord) arguments);
            } 
else {
                
return results;
            }
        }
    }

}

Tuesday, March 16, 2010

Running a SQL Script on startup in EclipseLink

Sometimes, when working with DDL generation it's useful to run a script to clean up the database first. In Hibernate if you put a file called "import.sql" on your classpath its contents will be sent to the database. Personally I'm not a fan of magic filenames but this can be a useful feature.

There's no built in support for this in EclipseLink but it's easy to do thank's to EclipseLink's high extensibility. Here's a quick solution I came up with: I simply register an event listener for the session postLogin event and in the handler I read a file and send each SQL statement to the database--nice and clean. I went a little further and supported setting the name of the file as a persistence unit property. You can specify this all in code or in the persistence.xml.

The ImportSQL class is configured as a SessionCustomizer through a persistence unit property which, on the postLogin event, reads the file identified by the "import.sql.file" property. This property is also specified as a persistence unit property which is passed to createEntityManagerFactory. This example also shows how you can define and use your own persistence unit properties.

import org.eclipse.persistence.config.SessionCustomizer;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventAdapter;
import org.eclipse.persistence.sessions.UnitOfWork;

public class ImportSQL implements SessionCustomizer {
    
    private void importSql(UnitOfWork unitOfWork, String fileName) {
        // Open file
        // Execute each line, e.g.,
        // unitOfWork.executeNonSelectingSQL("select 1 from dual");
    }

    @Override
    public void customize(Session session) throws Exception {
        session.getEventManager().addListener(new SessionEventAdapter() {
            @Override
            public void postLogin(SessionEvent event) {
                String fileName = (String) event.getSession().getProperty("import.sql.file");
                UnitOfWork unitOfWork = event.getSession().acquireUnitOfWork();
                importSql(unitOfWork, fileName);
                unitOfWork.commit() 
            }



        });
    }



    public static void main(String[] args) {
        Map<String, Object> properties = new HashMap<String, Object>();
        
        // Enable DDL Generation
        properties.put(PersistenceUnitProperties.DDL_GENERATION, PersistenceUnitProperties.DROP_AND_CREATE);
        properties.put(PersistenceUnitProperties.DDL_GENERATION_MODE, PersistenceUnitProperties.DDL_DATABASE_GENERATION);
        // Configure Session Customizer which will pipe sql file to db before DDL Generation runs
        properties.put(PersistenceUnitProperties.SESSION_CUSTOMIZER, "model.ImportSQL");
        properties.put("import.sql.file","/tmp/someddl.sql");
        
        EntityManagerFactory emf = Persistence
                .createEntityManagerFactory("employee", properties);

Thursday, February 18, 2010

EclipseLink @ OSGi DevCon & UKOUG Coherence London SIG

ImageNext week is a busy one for me in London with Gemini and EclipseLink related talks at both the OSGi DevCon (held in conjunction with JAX London) and the UKOUG Coherence London SIG.

At OSGi DevCon I'll be presenting Introducing the OSGi JPA Specification with Mike Kieth, the Gemini project lead, on the new specification and its reference implementation based on EclipseLink. This spec is part of the OSGi Enterprise Specification which extends the core OSGi platform to address enterprise scenarios.

At the UKOUG Coherence London SIG I'll be talking about TopLink Grid which integrates EclipseLink JPA with Oracle Coherence to support scaling JPA applications. If you've ever wondered how do I take my Java EE/JPA application and scale it to a cluster of hundreds of servers, this is the talk for you! As of now the session is fully booked but you can still get on the waiting list. ;-)

Monday, June 29, 2009

OSGi Persistence Slides from OSGi DevCon Europe 2009

Here are the slides to a short presentation I gave on persistence at OSGi DevCon Europe in Zurich. I had 30 minutes and wanted to do a demo to show that EclipseLink is running nicely in OSGi--it was tough cramming all this in but I did it without a moment to spare. Enjoy the slides and if they spark some questions you can post them here on the blog or on the EclipseLink newsgroup at http://www.eclipse.org/newsportal/thread.php?group=eclipse.rt.eclipselink or the eclipselink-users mailing list.
--Shaun

Tuesday, October 28, 2008

Delegation vs. Inheritance

The question "how do I change the type of my object" in JPA comes up now and then because developers know that an object's class is typically derived from an discriminator column on the database. They reason that if they can change the column value then class of the object can be changed. But it's not that simple.

What you're really trying to do is to change the class of an Entity object. Obviously this isn't something supported by Java nor by JPA. In EclipseLink it may be possible to hammer a discriminator column, invalidate the affected object in the cache, and then reread it. That could work but with a large number of caveats including ensuring you hold no references to the transformed object in your persistence context.

Instead, I'd suggest that if your domain model includes the ability to change the class of an Entity you refactor your model to use a delegation/role modeling approach rather than using inheritance. This allows you to change roles at runtime and even to have multiple concurrent roles (e.g., person could have a fireman role as well as a spouse role) and would behave accordingly.

Here's a simple example:

public enum PetType {
    CAT   { String speak() { return "meow"; } },
    DOG { String speak() { return "woof"; } };
    abstract String speak();
  }


@Entity
public class Pet {
    @Id
    @GeneratedValue
    private int id;
    private String name;
    
    public Pet() {
    }
    
    public Pet(String name) {
        super();
        this.name = name;
    }
    @Enumerated
    private PetType type = PetType.CAT;

        ...
    public String speak() {
        return this.getType().speak();
    }
}


        em.getTransaction().begin();
        Pet pet = new Pet("fluffy");
        System.out.println(pet.getName() + " is a " + pet.getType().toString() + " and says " + pet.speak());
        em.persist(pet);
        em.getTransaction().commit();
        
        em.getTransaction().begin();
        pet = em.find(Pet.class, pet.getId());
        pet.setType(PetType.DOG);
        System.out.println(pet.getName() + " is now a " + pet.getType().toString() + " and says " + pet.speak());
        em.getTransaction().commit();




fluffy is a CAT and says meow
[EL Fine]: Connection(27582163)--UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [50, SEQ_GEN]
[EL Fine]: Connection(27582163)--SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?
bind => [SEQ_GEN]
[EL Fine]: Connection(27582163)--INSERT INTO PET (ID, NAME, TYPE) VALUES (?, ?, ?)
bind => [101, fluffy, 0]
fluffy is now a DOG and says woof
[EL Fine]: Connection(27582163)--UPDATE PET SET TYPE = ? WHERE (ID = ?)
bind => [1, 101]

One more thing

You could also implement methods on the PetType that took the Pet object and called back to the appropriate Pet method depending on the PetType. All in all this is a pretty flexible solution.

Monday, May 26, 2008

EclipseLink at SpringOne 2008

This year I'll be at SpringOne again and this time I'll be co-presenting with Rob Harrop of SpringSource about using cool features of EclipseLink with Spring. I've blogged previously about using EclipseLink MOXy with Spring Web Services but with EclipseLink implementing a number of persistence standards and having so many advanced features there are lots of ways you can leverage it from Spring. We're going to dive into a few of the coolest features that differentiate EclipseLink from other persistence frameworks and show how you can use them in your Spring application.

--Shaun

Friday, May 16, 2008

Best JavaOne Shirt Ever!

If you've been to JavaOne in the last couple of years you'll have noticed the free custom T-Shirt booth. It's always jammed so I never paid it much attention. This year I happened to walk by it right after the trade show floor had opened--and the line was only 5 or 6 people--so I jumped in line! This year you had to complete an equation--remember this is a technology conference after all. The equation was:

"Java + _______ = _______".

Below you can see what I came up with. Best..(Free) T-Shirt...Ever!

--Shaun



ImageImage

The 1st Irish Open Source Technology Conference

Image
The Irish Open Source Technology Conference (IOTC) will be held June 18th-20th in Dublin and promises to be great event! I'll be there to represent EclipseLink and the Eclipse Runtime Project that it's a part of.

I'd been to the Irish Java Technology Conference last year which was also organized by Barry Alistair at IrishDev.com and so I know IOTC will be great. The conference venue was great, the speakers were top shelf (IMHO), and the Irish beer at the The Church was wonderful.

This year they'll be doing live streaming of sessions so if you can't make it to Dublin you can still "attend" sessions. Unfortunately the technology does not yet exist to stream the beer you should enjoy after a long day at the conference. ;-)

Friday, April 18, 2008

EclipseLink MOXy in Spring-WS

I was reminded that I'd promised an example of how to use EclipseLink MOXy in Spring Web Services so here we go. Fortunately this isn't hard to do as Spring-WS abstracts the concept of a Marshaller and an Unmarshaller so you can plug in various diverse technologies. One of those technologies supported "out of the box" is JAXB 2, and since EclipseLink MOXy implements JAXB 2 all we have to worry about is Spring configuration and just a little code.

I've adapted one of the Oracle TopLink 11g JAXB demos to use Spring for configuration and Spring-WS interfaces for marshalling/unmarshalling. That demo describes how the code works so, for brevity, I'll assume you've read it. A link to the source for this updated demo is at the bottom of this posting.

Marshalling/Unmarshalling is something you can do in a number of context in Spring-WS including handling WebService and JMS message payloads so once you see how MOXy JAXB is configured in this simple example you can use this knowledge in any Spring application that works with XML.

Configuration--Spring applicationContext.xml

Since this is Spring we need an applicationContext.xml file in which to
layout all your bean config. At the bottom of the file (below) I
declare two beans: marshallExample and unmarshallExample.
marshallExample has a marshaller injected, and unmarshallExample has
both a marshaller and unmarshaller injected. Notice that the bean
refs for both the marshaller and unmarshaller is the same bean
"jaxbMarshaller"--I'll get to that in a moment.

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="contextPath" value="examples.jaxb.generated"/>
    </bean>

    <bean id="marshallExample" class="examples.jaxb.marshall.MarshallGenerated">
        <property name="marshaller" ref="jaxbMarshaller"/>
     </bean>
 
    <bean id="unmarshallExample" class="examples.jaxb.unmarshall.UnmarshallGenerated">
        <property name="unmarshaller" ref="jaxbMarshaller"/>
        <property name="marshaller" ref="jaxbMarshaller"/>
     </bean>
</beans>

At the top of the file I declare "jaxbMarshaller" as an instance of the
Spring-WS Jaxb2Marshaller class. And I pass the property
"contextPath" to it so that it knows what classes it is responsible for
handing. In this case "examples.jaxb.generated". If you
look in that package in the example source you'll see a jaxb.properties
file. The jaxb.properties file can be used to specify the JAXB
implementation to use. In this case the jaxb.properties file
contains the single line:

javax.xml.bind.context.factory = org.eclipse.persistence.jaxb.JAXBContextFactory

This is how we plug in EclipseLink MOXy as the JAXB implementation to
use. The jaxb.properties file is standard JAXB, not Spring.
If you take a look back at the OTN example you'll see the same
file. What Spring-WS adds is the Jaxb2Marshaller class and the
generic interfaces it implements. Unlike in JAXB where you obtain
a JAXB Marshaller or Unmarshaller from a JAXBContext, Spring-WS does
this behind the scenes. Rather than exposing the objects it
instead exposes marshall and unmarshall methods. By not exposing
these objects it makes it possible to swap out a JAXB marshaller for
another kind of marshaller. And this is why jaxbMarshaller is used as
both marshaller and unmarshaller in the unmarshallExampleBean.

Bootstrapping Your Application

To use these beans you use the standard Spring bean lookup method, e.g.:

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        MarshallGenerated marshallGenerated = (MarshallGenerated) applicationContext
           .getBean("marshallExample", MarshallGenerated.class);

If you've got your applicationContext.xml right then the instance of
MarshallGenerated that you get from Spring will be fully configured
with an instance of Jaxb2Marshaller injected.

To marshall or unmarshall you use the Spring Jaxb2Marshaller methods. However the arguments to those methods are slightly different than those of a JAXB Marshaller or Unmarshaller and use javax.xml.transform.Source and javax.xml.transform.Result. This is a pretty minor difference and was an easy change to the application code.

Running the Example

To run the example you just need to download EclipseLinkMoxy-SpringWS.zip, unzip, and edit the environment variables eclipselink.home, spring.home, and spring-ws.home at the top of the ANT build.xml file. Once you have those set you can run ANT to build and run.

Tuesday, April 8, 2008

Passing DataSources to EclipseLink JPA

When you hear the same question twice it's time to blog the answer so the third time it comes up you can point to the blog. ;-) This time it's whether you can pass a DataSource to EclipseLink JPA and have it use that instead of what's configured in persistence.xml (or perhaps not configured in persistence.xml).

Yes, you can pass a DataSource. Here's a code sample that illustrates creating a Derby DataSource and passing it as a property to createEntityManagerFactory. Works like a charm.

--Shaun

private DataSource createDataSource() {
   ClientDataSource dataSource = new ClientDataSource();
   dataSource.setServerName("localhost");
   dataSource.setPortNumber(1527);
   dataSource.setDatabaseName("sample");
   dataSource.setUser("app");
   dataSource.setPassword("app");
   return dataSource;
}

private EntityManagerFactory getEntityManagerFactory() {
   if (emf == null) {
      Map properties = new HashMap();
      properties
         .put(PersistenceUnitProperties.NON_JTA_DATASOURCE,createDataSource());
      emf = Persistence.createEntityManagerFactory(PU_NAME, properties);
   }
   return emf;
}

Monday, March 17, 2008

EclipseLink is the JPA 2.0 Reference Implementation

I won't repeat the details here but you can check out the news over at the EclipseLink Team Blog and at the GlassFish Aquarium.

This is great news for JPA 2.0! And since EclipseLink shares the same source code "DNA" as TopLink Essentials, the JPA 1.0 reference implementation, migration for users of TopLink Essentials won't be difficult. Especially since EclipseLink includes tools for migrating code and configuration files from TopLink Essentials and Oracle TopLink.

IMHO, moving to EclipseLink is a no-brainer. EclipseLink has all the functionality of TopLink Essentials plus advanced features like cache coordination and advanced mappings like Converters , Returning Policies, and Basic Collections. Check it out!

--Shaun

Wednesday, January 30, 2008

Welcome (Back?)

With the open sourcing of Oracle TopLink in the EclipseLink (Eclipse Persistence Services) Project at Eclipse I thought I'd better get back to blogging and since my comments will be about more than TopLink I also thought it would be a good idea to move to a more general blog name. So welcome to On Persistence!

--Shaun

Customizing EclipseLink JPA/ORM Relationship Joins

Sometimes you want to add join criteria in a one-to-one or one-to-many mapping with an arbitrary constraint. There are a few situations in which you'd want to do this but I won't go into that here. But hopefully when it happens to you you'll know how to deal with it in EclipseLink.

As an example, let's consider a two class object model with classes A and B. A has a one-to-one with B based on a foreign key relationship. Let's say that we only ever want the one-to-one to B to resolve to an object if the B's status is "active". What we need to do is ensure that the SQL query for B includes this constraint.

Behinds the Scenes

When you define a one-to-one or one-to-many mapping in either JPA (e.g., @OneToOne) or using the EclipseLink native mapping format, behind the scenes EclipseLink builds a Mapping object. In the Mapping object is an Expression that defines the selection criteria that will be used to resolve the object or objects that are the target of the mapping. At runtime EclipseLink generates SQL from this Expression. So the key to changing the SQL used in the join is to augment or change in some way this Expression.

Making it Work

To get our hands on the Expression of the Mapping object we need to define a Descriptor Customizer. In my TopLink blog I described how to define a TopLink customizer to make a class read-only. We'll take the same approach here with EclipseLink. There are only two things you need to do to get this working:
  1. Create a customizer class that implements org.eclipse.persistence.internal.sessions.factories.DescriptorCustomizer
  2. Associate the customizer with class A in the persistence.xml
Here's a customizer that adds a constraint on the "status" property of the target object (error handling left out for brevity):

public class ACustomizer implements DescriptorCustomizer {

public void customize(ClassDescriptor desc) {
OneToOneMapping mapping = (OneToOneMapping) desc.getMappingForAttributeName("b");
Expression origExp = mapping.buildSelectionCriteria();
ExpressionBuilder b = origExp.getBuilder();
Expression constantExp = b.get("status").equal("active");
Expression newExp= origExp.and(constantExp);
mapping.setSelectionCriteria(newExp);
}
}
In the customizer we get the ClassDescriptor associated with class A that EclipseLink built after processing all annotations and mapping files. From it we get the OneToOneMapping for the "b" attribute. And then we get the selection criteria expression that is based on the mappings. From the Expression we get the ExpressionBuilder and proceed to build a new Expression that we "and" with the original Expression. To finish up we set the selection criteria of the OneToOneMapping to the new Expression.

To enable the use of the customizer you'll need to add a property to your persistence.xml.

<properties>
<property name="eclipselink.descriptor.customizer.A"
value="model.ACustomizer"/>

When I setup this customizer in my example I see the following SQL in the console when I try to read the B associated with an A:

SELECT ID, STATUS FROM B WHERE ((ID = 2) AND (STATUS = 'active'))

Cool--just what I wanted!

--Shaun