Enterprise Java

Extending your JPA POJOs

Extensibility is an important characteristic in many architectures.  It is a measure of how easy (or difficult)
it is to add or change functionality without impacting existing core system functionality.

Let’s take a simple example.  Suppose your company have a core product to track all the users in a sports club.  Within your product architecture, you have a domain model represented by JPA POJOs.  The domain model contains many POJOs including – of course – a User POJO.

package com.alex.staveley.persistence
/**
 * User entity.  Represents Users in the Sports Club. 
 * * Note: The SQL to generate a table for this in MySQL is:
 *
 * CREATE TABLE USER (ID INT NOT NULL auto_increment, NAME varchar(255) NOT NULL, 
 *  PRIMARY KEY (ID)) ENGINE=InnoDB;
 */ 
@Entity
public class User {
    /* Surrogate Key - automatically generated by DB. */ 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Id
    private int id;
 
    private String name;

    public int getId() {
        return id;
    }
 
    public void setName(String name) {
        this.name=name;
    }
 
    public String getName() {
        return name;
    }
}

Now, some customers like your product but they need some customisations done before they buy it.  For example,
one customer wants the attribute birthplace added to the User and wants this persisted.  The logical place for this attribute is – of course – in the User POJO, but no other customer wants this attribute.  So what do you do?

Do you make a specific User class just for this customer and then swap it in just for them?  What happens
when you change your Product User class then?  What happens if another customer wants another customisation?
Or changes their mind?  Are you sensing things are going to get messy?

Thankfully, one implementation of JPA: Eclipselink helps out here. The 2.3 release (available since June 2011, latest release being a 2.3.2 maintenance released just recently, 9th December, 2011) includes some very features which work a treat for this type of scenario. Let’s elaborate. By simply adding the @VirtualAccessmethods Eclipselink annotation to a POJO we signal to Eclipselink that the POJO may have some extra (also known as virtual) attributes. You don’t have to specify any of these extra attributes in code, otherwise they wouldn’t be very virtual! You just have to specify a generic getter and setter to cater for their getting and setting. You also have to have somewhere to store them in memory, something like a good old hashmap – which of course should be transient because we don’t persist the hashmap itself. Note: They don’t have to be stored in a HashMap, it’s just a popular choice!

Let’s take a look at our revamped User which is now extensible!

@Entity
@VirtualAccessMethods
public class User {
    /* Surrogate Key - automatically generated by DB. */
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Id
    private int id;
  
    private String name;
  
    @Transient
    private Map<String, Object> extensions = new HashMap();
 
    public int getId() {
        return id;
    }
  
    public void setName(String name) {
        this.name=name;
    }
  
    public String getName() {
        return name;
    }
  
    public <t> T get(String name) {
        return (T) extensions.get(name);
    }
  
    public Object set(String name, Object value) {
        return extensions.put(name, value);
    } 
}

So, is that it?  Well there’s a little bit more magic.  You have to tell eclipselink about your additional attributes.  More specifically: what their names and datatypes are.
You do this by updating your eclipselink-orm.xml which resides in the same META-INF folder that the persistent.xml is in.

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.eclipse.org/eclipselink/xsds/persistence/orm 
    http://www.eclipse.org/eclipselink/xsds/eclipselink_orm_2_1.xsd"
    version="2.1">
 
    <entity class="com.alex.staveley.persistence.User">
        <attributes>
     <basic name="thebirthplace" attribute-type="String" access="VIRTUAL">
                <column name="birthplace"/>
                <access-methods get-method="get" set-method="set"/>
            </basic>
        </attributes>
    </entity> 
</entity-mappings>

Now this configuration simply states, the User entity has an additional attribute which in java is “thebirthplace” and it is virtual.  This means it is not explictly defined in the POJO but if we were to debug things, we’d see the attribute having the name ‘thebirthplace’ in memory.

This configuration also states that the corresponding database column for the attribute is birthplace. 
And eclipselink can get and set this method by using the generic get /set methods.

You wanna test it?

Well add the column to your database table.  In MySql this would be:

        alter table user add column birthplace varchar(64)

Then run this simple test:

@Test
public void testCreateUser() {
    User user = new User();
    user.setName("User1Name");
    user.set("thebirthplace", "donabate");
    entitymanager.getTransaction().begin();
    entitymanager.persist(user);
    entitymanager.getTransaction().commit();
    entitymanager.close();
}

So now, we can have one User POJO in our product code which is extensible.  Each customer can have their own attributes added to the User – as they wish.  And of course, each customer is separated from
all other customers very easily by just ensuring each customer’s extensions resides in a specific eclipslink-orm.xml.  Remember, you are free to name these files as you want and if you don’t use the default names you just update the persistence.xml file to state what names you are using

This approach means that when we want to update User in our product, we only have to update one and only User POJO (because we have ensured there is only one).  But when specific attributes have to be added for specific customer(s), we don’t touch the User POJO code.  We simple make the changes to the XML and do not have to recompile anything from the core product. And of course, at any time it is easy to see what the customisations are for any customer by just simply looking at the appropriate eclipselink-orm.file.

Ye Ha. Happy Extending! 

References:

  1. Extending your JPA POJOs   from our JCG partner Alex Staveley  at the Dublin’s Tech Blog 
  2. http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Extensible_Entities
  3. http://www.eclipse.org/eclipselink/

Related Articles :

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button