JPA : Persistable Java Types

When persisting a class, a persistence solution needs to know how to persist the types of each field in the class. Clearly a persistence solution can only support a finite number of Java types; it cannot know how to persist every possible type creatable. The JPA specification define lists of types that are required to be supported by all implementations of those specifications. This support can be conveniently split into two parts

  • Primary Types : An object that can be referred to (object reference, providing a relation) and that has an "identity" is termed a primary type. DataNucleus supports the following Java types as primary.
    • persistable : any Entity that can be persisted with its own identity in the datastore
    • interface where the interface field represents an Entity
    • java.lang.Object where the field represents an Entity
  • Secondary Types : An object that does not have an "identity" is termed a secondary type. This is something like a String or Date field in a class, or alternatively a Collection (that contains other objects), or an embedded Entity. The sections below shows the currently supported secondary java types in DataNucleus. The tables in these sections show
    • Extension? : whether the type is JPA standard, or is a DataNucleus extension
    • EAGER : whether the field is retrieved by default when retrieving the object itself.
    • Persistent : whether the field is persisted by default, or whether the user has to mark the field as persistent in XML/annotations to persist it
    • Proxied : whether the field is represented by a "proxy" that intercepts any operations to detect whether it has changed internally (such as Collection, Map).
    • PK : whether the field can be used as part of the primary-key

If you have support for any additional types and would either like to contribute them, or have them listed here, let us know. Supporting a new type is easy, typically involving a JPA AttributeConverter if you can easily convert the type into a String or Long. See also the Java Types plugin-point You can also define more specific support for it with RDBMS datastores - See the RDBMS Java Types plugin-point

Handling of second-class types uses wrappers and bytecode enhancement with DataNucleus. This contrasts to what Hibernate uses (proxies), and what Hibernate imposes on you. See this blog entry if you have doubts about this approach.


Primitive and java.lang Types

All primitive types and wrappers are supported and will be persisted into a single database "column". Arrays of these are also supported, and can either be serialised into a single column, or persisted into a join table (dependent on datastore).

Java Type Extension? EAGER? Persistent? Proxied? PK? Comments
boolean Persisted as BOOLEAN, Integer (i.e 1,0), String (i.e 'Y','N').
byte
char
double
float
int
long
short
java.lang.Boolean Persisted as BOOLEAN, Integer (i.e 1,0), String (i.e 'Y','N').
java.lang.Byte
java.lang.Character
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Short
java.lang.Number Persisted in a column capable of storing a BigDecimal, and will store to the precision of the object to be persisted. On reading back the object will be returned typically as a BigDecimal since there is no mechanism for determing the type of the object that was stored.
java.lang.String
java.lang.StringBuffer Persisted as String. The dirty check mechanism for this type is limited to immutable mode, which means if you change a StringBuffer object field, you must reassign it to the owner object field to make sure changes are propagated to the database.
java.lang.StringBuilder Persisted as String. The dirty check mechanism for this type is limited to immutable mode, which means if you change a StringBuffer object field, you must reassign it to the owner object field to make sure changes are propagated to the database.
java.lang.Class Persisted as String.

java.math types

BigInteger and BigDecimal are supported and persisted into a single numeric column by default.

Java Type Extension? EAGER? Persistent? Proxied? PK? Comments
java.math.BigDecimal Persisted as DOUBLE or String. String can be used to retain precision.
java.math.BigInteger Persisted as INTEGER or String. String can be used to retain precision.

Temporal Types (java.util, java.sql. java.time, Jodatime)

DataNucleus supports a very wide range of temporal types, with flexibility in how they are persisted.

Java Type Extension? EAGER? Persistent? Proxied? PK? Comments
java.sql.Date Persisted as DATE, String, DATETIME or Long.
java.sql.Time Persisted as TIME, String, DATETIME or Long.
java.sql.Timestamp Persisted as TIMESTAMP, String or Long.
java.util.Calendar Persisted as TIMESTAMP (inc Timezone), DATETIME, String, or as (Long, String) storing millis + timezone respectively
java.util.GregorianCalendar Persisted as TIMESTAMP (inc Timezone), DATETIME, String, or as (Long, String) storing millis + timezone respectively
java.util.Date Persisted as DATETIME, String or Long.
java.util.TimeZone Persisted as String.
java.time.LocalDateTime Persisted as DATETIME, String, or Timestamp.
java.time.LocalTime Persisted as TIME, String, or Long.
java.time.LocalDate Persisted as DATE, String, or DATETIME.
java.time.OffsetDateTime Persisted as Timestamp, String, or DATETIME.
java.time.OffsetTime Persisted as TIME, String, or Long.
java.time.MonthDay Persisted as String, DATE, or as (Integer,Integer) with the latter being month+day respectively.
java.time.YearMonth Persisted as String, DATE, or as (Integer,Integer) with the latter being year+month respectively.
java.time.Year Persisted as Integer, or String.
java.time.Period Persisted as String.
java.time.Instant Persisted as TIMESTAMP, String, Long, or DATETIME.
java.time.Duration Persisted as String, Double (secs.nanos), or Long (secs).
java.time.ZoneId Persisted as String.
java.time.ZoneOffset Persisted as String.
java.time.ZonedDateTime Persisted as Timestamp, or String.
org.joda.time.DateTime Requires datanucleus-jodatime plugin. Persisted as TIMESTAMP or String.
org.joda.time.LocalTime Requires datanucleus-jodatime plugin. Persisted as TIME or String.
org.joda.time.LocalDate Requires datanucleus-jodatime plugin. Persisted as DATE or String.
org.joda.time.LocalDateTime Requires datanucleus-jodatime plugin. Persisted as TIMESTAMP, or String.
org.joda.time.Duration Requires datanucleus-jodatime plugin. Persisted as String or Long.
org.joda.time.Interval Requires datanucleus-jodatime plugin. Persisted as String or (TIMESTAMP, TIMESTAMP).
org.joda.time.Period Requires datanucleus-jodatime plugin. Persisted as String.

Collection/Map types

DataNucleus supports a very wide range of collection, list and map types.

Java Type Extension? EAGER? Persistent? Proxied? PK? Comments
java.util.ArrayList See the 1-N Lists Guide
java.util.BitSet Persisted as collection by default, but will be stored as String when the datastore doesn't provide for collection storage
java.util.Collection See the 1-N Collections Guide
java.util.HashMap See the 1-N Maps Guide
java.util.HashSet See the 1-N Collections Guide
java.util.Hashtable See the 1-N Maps Guide
java.util.LinkedHashMap Persisted as a Map currently. No List-ordering is supported. See the 1-N Maps Guide
java.util.LinkedHashSet Persisted as a Set currently. No List-ordering is supported. See the 1-N Collections Guide
java.util.LinkedList See the 1-N Lists Guide
java.util.List See the 1-N Lists Guide
java.util.Map See the 1-N Maps Guide
java.util.Properties See the 1-N Maps Guide
java.util.PriorityQueue The comparator is specifiable via the metadata extension comparator-name (see below). See the 1-N Lists Guide
java.util.Queue The comparator is specifiable via the metadata extension comparator-name (see below). See the 1-N Lists Guide
java.util.Set See the 1-N Collections Guide
java.util.SortedMap The comparator is specifiable via the metadata extension comparator-name (see below). See the 1-N Maps Guide
java.util.SortedSet The comparator is specifiable via the metadata extension comparator-name (see below). See the 1-N Collections Guide
java.util.Stack See the 1-N Lists Guide
java.util.TreeMap The comparator is specifiable via the metadata extension comparator-name (see below). See the 1-N Maps Guide
java.util.TreeSet The comparator is specifiable via the metadata extension comparator-name (see below). See the 1-N Collections Guide
java.util.Vector See the 1-N Lists Guide
com.google.common.collect.Multiset Requires datanucleus-guava plugin. See the 1-N Collections Guide

Comparators

Containers that support a Comparator to order the elements of the set can specify it in metadata like this.

    @OneToMany
    @Extension(key="comparator-name", value="mydomain.model.MyComparator")
    SortedSet<MyElementType> elements; 

When instantiating the SortedSet field, it will create it with a comparator of the specified class (which must have a default constructor).


Enums

By default an Enum is persisted as either a String form (the name), or as an integer form (the ordinal). You control which form by specifying the @Enumerated annotation (or equivalent XML).

Java Type Extension? EAGER? Persistent? Proxied? PK? Comments
java.lang.Enum Persisted as String (name) or int (ordinal). Specified via @Enumerated annotation or equivalent XML.

A DataNucleus extension to this is where you have an Enum that defines its own "value"s for the different enum options. Note that this is applicable to RDBMS, MongoDB, Cassandra, Neo4j, HBase, Excel, ODF and JSON currently.

public enum MyColour 
{
    RED((short)1), GREEN((short)3), BLUE((short)5), YELLOW((short)8);

    private short value;

    private MyColour(short value)
    {
        this.value = value;
    }

    public short getValue() 
    {
        return value;
    }
}

With the default persistence it would persist as String-based, so persisting "RED" "GREEN" "BLUE" etc. With @Enumerated as ORDINAL it would persist 0, 1, 2, 3 being the ordinal values. If you define the metadata as

@Extension(key="enum-value-getter", value="getValue")
MyColour colour;

this will now persist 1, 3, 5, 8, being the "value" of each of the enum options. You can use this method to persist "int", "short", or "String" types.

A DataNucleus extension is available for RDBMS datastores where you are storing the name of the enum, and to put a CHECK constraint on the column. You specify it like this

@Extension(key="enum-check-constraint", value="true")
MyColour colour;

Geospatial Types

DataNucleus has extensive support for Geospatial types. Here we present the core Java geospatial types, but you can find more specialised Geospatial types covered on the GeoSpatial types page.

Java Type Extension? EAGER? Persistent? Proxied? PK? Comments
java.awt.Point Requires datanucleus-geospatial plugin. Persisted as (int, int) on RDBMS, or as String elsewhere.
java.awt.Rectangle Requires datanucleus-geospatial plugin. Persisted as (int, int, int, int) on RDBMS, or as String elsewhere.
java.awt.Polygon Requires datanucleus-geospatial plugin. Persisted as (int[], int[], int) on RDBMS, or as String elsewhere.
java.awt.geom.Line2D Requires datanucleus-geospatial plugin. Persisted as (double, double, double, double) or (float, float, float, float) on RDBMS, or as String elsewhere.
java.awt.geom.Point2D Requires datanucleus-geospatial plugin. Persisted as (double, double) or (float, float) on RDBMS, or as String elsewhere.
java.awt.geom.Rectangle2D Requires datanucleus-geospatial plugin. Persisted as (double, double, double, double) or (float, float, float, float) on RDBMS, or as String elsewhere.
java.awt.geom.Arc2D Requires datanucleus-geospatial plugin. Persisted as (double, double, double, double, double, double, int) or (float, float, float, float, float, float, int) on RDBMS, or as String elsewhere.
java.awt.geom.CubicCurve2D Requires datanucleus-geospatial plugin. Persisted as (double, double, double, double, double, double, doubel, double) or (float, float, float, float, float, float, float, float) on RDBMS, or as String elsewhere.
java.awt.geom.Ellipse2D Requires datanucleus-geospatial plugin Persisted as (double, double, double, double) or (float, float, float, float) on RDBMS, or as String elsewhere.
java.awt.geom.QuadCurve2D Requires datanucleus-geospatial plugin. Persisted as (double, double, double, double, double, double) or (float, float, float, float, float, float) on RDBMS, or as String elsewhere.
java.awt.geom.RoundRectangle2D Requires datanucleus-geospatial plugin. Persisted as (double, double, double, double, double, double) or (float, float, float, float, float, float) on RDBMS, or as String elsewhere.

Other Types

Many other types are supported.

Java Type Extension? EAGER? Persistent? Proxied? PK? Comments
java.lang.Object Either persisted serialised, or represents multiple possible types
java.util.Currency Persisted as String.
java.util.Locale Persisted as String.
java.util.UUID Persisted as String, or alternatively as native uuid on PostgreSQL when specifying sql-type="uuid".
java.util.Optional<type> Persisted as the type of the generic type that optional represents.
java.awt.Color Persisted as String or as (Integer,Integer,Integer,Integer) storing red,green,blue,alpha respectively.
java.awt.image.BufferedImage Persisted as serialised.
java.net.URI Persisted as String.
java.net.URL Persisted as String.
java.io.Serializable Persisted as serialised.
java.io.File Only for RDBMS, persisted to LONGVARBINARY, and retrieved as streamable so as not to adversely affect memory utilisation, hence suitable for large files.

Arrays

The vast majority of the secondary types can also be persisted as arrays of that type as well. Here we list a few of the combinations definitely supported as arrays, but others likely will work fine

Java Type Extension? EAGER? Persistent? Proxied? PK? Comments
boolean[] See the Arrays Guide
byte[] See the Arrays Guide
char[] See the Arrays Guide
double[] See the Arrays Guide
float[] See the Arrays Guide
int[] See the Arrays Guide
long[] See the Arrays Guide
short[] See the Arrays Guide
java.lang.Boolean[] See the Arrays Guide
java.lang.Byte[] See the Arrays Guide
java.lang.Character[] See the Arrays Guide
java.lang.Double[] See the Arrays Guide
java.lang.Float[] See the Arrays Guide
java.lang.Integer[] See the Arrays Guide
java.lang.Long[] See the Arrays Guide
java.lang.Short[] See the Arrays Guide
java.lang.String[] See the Arrays Guide
java.util.Date[] See the Arrays Guide
java.math.BigDecimal[] See the Arrays Guide
java.math.BigInteger[] See the Arrays Guide
java.lang.Enum[] See the Arrays Guide
java.util.Locale[] See the Arrays Guide
Entity[] See the Arrays Guide

Generic Type Variables

JPA does not explicitly require support for generic type variables. DataNucleus does support some situations with generic type variables.

The first example that is largely supported is where you have an abstract base class with a generic TypeVariable and then you specify the type in the (concrete) subclass(es).

@MappedSuperclass
public abstract class Base<T>
{
    private T id;
}

@Entity
public class Sub1 extends Base<Long>
{
    ...
}

@Entity
public class Sub2 extends Base<Integer>
{
    ...
}

Similarly you use TypeVariables to form relations, like this

@MappedSuperclass
public abstract class Ownable<T extends Serializable> implements Serializable
{
    @ManyToOne(optional = false)
    private T owner;
}

@Entity
public class Document extends Ownable<Person>
{
    ...
}

Clearly there are many combinations of where TypeVariables can be used and DataNucleus supports a subset of these currently. Try it and see.

JPA Attribute Converters

JPA2.1 introduces an API for conversion of an attribute of an Entity to its datastore value. You can define a "converter" that will convert to the datastore value and back from it, implementing this interface.

public interface AttributeConverter<X,Y>
{
    public Y convertToDatabaseColumn (X attributeObject);

    public X convertToEntityAttribute (Y dbData);
}

so if we have a simple converter to allow us to persist fields of type URL in a String form in the datastore, like this

public class URLStringConverter implements AttributeConverter<URL, String>
{
    public URL convertToEntityAttribute(String str)
    {
        if (str == null)
        {
            return null;
        }

        URL url = null;
        try
        {
            url = new java.net.URL(str.trim());
        }
        catch (MalformedURLException mue)
        {
            throw new IllegalStateException("Error converting the URL", mue);
        }
        return url;
    }

    public String convertToDatabaseColumn(URL url)
    {
        return url != null ? url.toString() : null;
    }
}

and now in our Entity class we mark any URL field as being converted using this converter

@Entity
public class MyClass
{
    @Id
    long id;

    @Basic
    @Convert(converter=URLStringConverter.class)
    URL url;

    ...
}

Note that in strict JPA 2.1 you have to mark all converters with the @Converter annotation. In DataNucleus if you specify the converter class name in the @Convert then we know its a converter so don't really see why we need a user to annotate the converter too. We only require annotation as @Converter if you want the converter to always be applied to fields of a particular type. i.e if you want all URL fields to be persisted using the above converter (without needing to put @Convert on each field of that type) then you would add the annotation

@Converter(autoApply=true)
public class URLStringConverter implements AttributeConverter<URL, String>
{
    ...
}

Note that if you have some java type with a @Converter registered to "autoApply", you can turn it off on a field-by-field basis with

    @Convert(disableConversion=true)
    URL url;

A further use of AttributeConverter is where you want to apply type conversion to the key/value of a Map field, or to the element of a Collection field. The Collection element case is simple, you just specify the @Convert against the field and it will be applied to the element. If you want to apply type conversion to a key/value of a map do this.

    @Convert(attributeName="key", converter=URLStringConverter.class)
    Map<URL, OtherEntity> myMap;

So we specify the attributeName to be key, and to use it on the value we would set it to value.