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 :

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


eight − = 3



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close