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


First-Class (FCO) Types

An object that can be referred to (object reference, providing a relation) and that has an "identity" is termed a First Class Object (FCO). DataNucleus supports the following Java types as FCO

  • persistable : any class marked for persistence (annotations or XML) can be persisted with its own identity in the datastore
  • interface where the field represents a persistable object
  • java.lang.Object where the field represents a persistable object

Supported Second-Class (SCO) Types

An object that does not have an "identity" is termed a Second Class Object (SCO). This is something like a String or Date field in a class, or alternatively a Collection (that contains other objects). The table below shows the currently supported SCO java types in DataNucleus. The table shows

  • 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 (this equates to the "default-fetch-group" concept in JDO)
  • persistence-modifier : 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.
  • primary-key : whether the field can be used as part of the primary-key

Java Type Extension? EAGER? Persistent? Proxied? PK? Plugin
boolean datanucleus-core
byte datanucleus-core
char datanucleus-core
double datanucleus-core
float datanucleus-core
int datanucleus-core
long datanucleus-core
short datanucleus-core
boolean[] datanucleus-core
byte[] datanucleus-core
char[] datanucleus-core
double[] datanucleus-core
float[] datanucleus-core
int[] datanucleus-core
long[] datanucleus-core
short[] datanucleus-core
java.lang.Boolean datanucleus-core
java.lang.Byte datanucleus-core
java.lang.Character datanucleus-core
java.lang.Double datanucleus-core
java.lang.Float datanucleus-core
java.lang.Integer datanucleus-core
java.lang.Long datanucleus-core
java.lang.Short datanucleus-core
java.lang.Boolean[] datanucleus-core
java.lang.Byte[] datanucleus-core
java.lang.Character[] datanucleus-core
java.lang.Double[] datanucleus-core
java.lang.Float[] datanucleus-core
java.lang.Integer[] datanucleus-core
java.lang.Long[] datanucleus-core
java.lang.Short[] datanucleus-core
java.lang.Number [4] datanucleus-core
java.lang.Object datanucleus-core
java.lang.String datanucleus-core
java.lang.StringBuffer [3] datanucleus-core
java.lang.String[] datanucleus-core
java.lang.Class datanucleus-core
java.math.BigDecimal datanucleus-core
java.math.BigInteger datanucleus-core
java.math.BigDecimal[] datanucleus-core
java.math.BigInteger[] datanucleus-core
java.sql.Date datanucleus-core
java.sql.Time datanucleus-core
java.sql.Timestamp datanucleus-core
java.util.ArrayList datanucleus-core
java.util.BitSet datanucleus-core
java.util.Calendar datanucleus-core
java.util.Collection datanucleus-core
java.util.Currency datanucleus-core
java.util.Date datanucleus-core
java.util.Date[] datanucleus-core
java.util.GregorianCalendar [7] datanucleus-core
java.util.HashMap datanucleus-core
java.util.HashSet datanucleus-core
java.util.Hashtable datanucleus-core
java.util.LinkedHashMap [5] datanucleus-core
java.util.LinkedHashSet [6] datanucleus-core
java.util.LinkedList datanucleus-core
java.util.List datanucleus-core
java.util.Locale datanucleus-core
java.util.Locale[] datanucleus-core
java.util.Map datanucleus-core
java.util.Properties datanucleus-core
java.util.PriorityQueue datanucleus-core
java.util.Queue datanucleus-core
java.util.Set datanucleus-core
java.util.SortedMap [2] datanucleus-core
java.util.SortedSet [1] datanucleus-core
java.util.Stack datanucleus-core
java.util.TimeZone datanucleus-core
java.util.TreeMap [2] datanucleus-core
java.util.TreeSet [1] datanucleus-core
java.util.UUID datanucleus-core
java.util.Vector datanucleus-core
java.awt.Color datanucleus-core
java.awt.image.BufferedImage datanucleus-core
java.awt.Point datanucleus-geospatial
java.awt.Rectangle datanucleus-geospatial
java.net.URI datanucleus-core
java.net.URL datanucleus-core
java.io.Serializable datanucleus-core
java.io.File [8] datanucleus-rdbms
persistable datanucleus-core
persistable[] datanucleus-core
java.lang.Enum datanucleus-core
java.lang.Enum[] datanucleus-core
java.time.LocalDateTime datanucleus-java8
java.time.LocalTime datanucleus-java8
java.time.LocalDate datanucleus-java8
java.time.MonthDay datanucleus-java8
java.time.YearMonth datanucleus-java8
java.time.Year datanucleus-java8
java.time.Period datanucleus-java8
java.time.Instant datanucleus-java8
java.time.Duration datanucleus-java8
java.time.ZoneId datanucleus-java8
java.time.ZoneOffset datanucleus-java8
org.joda.time.DateTime datanucleus-jodatime
org.joda.time.LocalTime datanucleus-jodatime
org.joda.time.LocalDate datanucleus-jodatime
org.joda.time.Duration datanucleus-jodatime
org.joda.time.Interval datanucleus-jodatime
org.joda.time.Period datanucleus-jodatime
com.google.common.collect.Multiset datanucleus-guava
  • [1] - java.util.SortedSet, java.util.TreeSet allow the specification of comparators via the "comparator-name" DataNucleus extension MetaData element (within <collection>). The headSet, tailSet, subSet methods are only supported when using cached collections.
  • [2] - java.util.SortedMap, java.util.TreeMap allow the specification of comparators via the "comparator-name" DataNucleus extension MetaData element (within <map>). The headMap, tailMap, subMap methods are only supported when using cached containers.
  • [3] - java.lang.StringBuffer dirty check mechanism is limited to immutable mode, it 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.
  • [4] - java.lang.Number will be stored 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.
  • [5] - java.util.LinkedHashMap treated as a Map currently. No List-ordering is supported.
  • [6] - java.util.LinkedHashSet treated as a Set currently. No List-ordering is supported.
  • [7] - java.util.Calendar is, by default, stored in one column (Timestamp - assumes that this stores the TimeZone) but can be stored into two columns (millisecs, Timezone) if requested.
  • [8] - available only for RDBMS, persisted into LONGVARBINARY, and retrieved as streamable so as not to adversely affect memory utilisation, hence suitable for large files. New in 3.2.8 of datanucleus-rdbms

Note that support is available for persisting other types depending on the datastore to which you are persisting

If you have support for any additional types and would either like to contribute them, or have them listed here, let us know


You can add support for other basic Java types quite easily, particularly if you can store it as a String or Long and then retrieve it back into its object form from that - See the Java Types plugin-point You can also define more specific support for it with RDBMS datastores - See the RDBMS Java Types plugin-point


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>
{
    ...
}

The only other point to make is 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;

Eclipse EMF models

You could try to persist Eclipse EMF models using the Texo project to generate POJOs