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



The 1st Irish Open Source Technology Conference


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