Datanucleus Accessplatform Docs
Datanucleus Accessplatform Docs
DataNucleus AccessPlatform
v. 4.0
User Guide
......................................................................................................................................
DataNucleus 2014-12-17
Table of Contents i
Table of Contents
.......................................................................................................................................
1. Table of Contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
2. General . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2..1. What's New . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2..2. Upgrade Migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2..3. Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2..4. Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2..5. Persistence API Choice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2..6. Development Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2..7. Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2..8. Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2..9. ORM Relationships . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2..10. Persistence Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2..11. Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
2..12. Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3. Datastore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3..1. Supported Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3..2. RDBMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
3..2..1. Java Types (Spatial) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3..2..2. Datastore Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3..2..3. Failover . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
3..2..4. Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3..2..5. JDOQL : Spatial Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
3..2..6. Statement Batching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
3..2..7. Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
3..2..8. Datastore API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
3..3. ODF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
3..4. Excel (XLS) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
3..5. Excel (OOXML) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
3..6. XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
3..7. HBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
3..8. MongoDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
3..9. Cassandra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
3..10. Neo4j . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
3..11. JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
3..12. Amazon S3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
3..13. GoogleStorage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
3..14. LDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
3..14..1. Relations by DN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
1 General
.......................................................................................................................................
DataNucleus AccessPlatform v4.0 provides persistence and retrieval of Java objects to a range of
datastores using JDO/ JPA/ REST APIs, with a range of query languages and is fully-compliant
with JDO and JPA specifications. It is Apache 2 licensed. No other persistence solution offers the
same range of APIs, datastores and query languages whilst also being fully compliant.
icon.
2 What's New
.......................................................................................................................................
3 Upgrade Migration
.......................................................................................................................................
• HBase : previously all primitives were stored serialised. Set the metadata 'serialized' flag on the
field/property to continue doing that.
• Queries are no longer run in a separate thread (which was the previous way of supporting query
cancellation, now reworked for RDBMS to use SQL error codes).
• Persistence properties for schema validation datanucleus.validateXXX now default to false
• The metadata extension index that is used to specify a column position (in table) was previously
required under "field" for Excel/ODF plugins. It should be under "column" now
• Default allocation size for increment and sequence value strategies have been changed for
JDO usage to 10 and 10 respectively (from 5 and 1). You can configure the global defaults via
persistence properties
• The JDOQL implementation used for RDBMS is now the rewritten "generic" implementation.
To use the old implementation, set the JDOQL implementation as "JDOQL-Legacy"
• Use of JPA should be run against the JPA2 "final" jar (or its Apache Geronimo specs equivalent)
• Heavy refactoring has been done internally so if relying on DataNucleus APIs you should
check against SVN for changes. In particular, plugins should be using ObjectProvider instead of
StateManager, and ExecutionContext in place of ObjectManager.
4 Getting Started
.......................................................................................................................................
1. Decide which datastore your project will use, and then download DataNucleus AccessPlatform
2. Depending on which ZIP you downloaded above, and what add-ons you'll be using you may also
need to download some dependencies
You now have the necessary components to start investigating use of DataNucleus.
4.1.2 Starting up
Decide which persistence API you want to use. If you're not familiar with these APIs then the
next thing to do is to learn about JDO and JPA, or alternatively REST. You need to understand
the basic concepts involved. There is plenty of reading on the internet, starting with the JDO or JPA
specifications of course.
The best thing to do after some reading is to try the JDO Tutorial (for RDBMS, HBase,
MongoDB, Neo4j, ODF, Excel) or try the JPA Tutorial (for RDBMS, HBase, MongoDB,
Neo4j, ODF, Excel). These explain the basic steps of applying JDO/JPA (and DataNucleus)
to your own application. The source code from the tutorials is available for download. Please
download it and start up your development environment with the Tutorial classes and files. Once
you have completed the Tutorial you're in a position to apply DataNucleus JDO/JPA to your own
application, and start benefiting from what it offers.
• To persist objects of classes you firstly need to define which classes are persistable, and how
they are persisted. Start under the JDO Class Mapping and JPA Class Mapping sections
5 Dependencies
.......................................................................................................................................
• Target datastore : JDO is designed for all datastores, whereas JPA is just designed around
RDBMS and explicitly uses RDBMS/SQL terminology. If using RDBMS then you have the
choice. If using, for example, a NoSQL store then JDO makes much more sense
• Datastore interoperability : are you likely to change your datastore type at some point in the
future ? If so you likely ought to use JDO due to its design
• API : both APIs are very similar. JDO provides more options and control though for basic
persistence and retrieval there are differences only in the namings
• ORM : JDO has a more complete ORM definition, as shown on Apache JDO ORM Guide
• Experience : do your developers know a particular API already ? As mentioned the API's
themselves are very similar, though the metadata definition is different. Remember that you can
use JPA metadata with the JDO API, and vice-versa.
• Querying : do you need a flexible query language that is object-oriented and extensible ?
JDOQL provides this and the implementation in DataNucleus allows extensions. If you just want
SQL then you can use JDO or JPA since both provide this
• Fetch Control : do you need full control of what is fetched, and when ? JDO provides fetch
groups, whereas JPA2.1 now provides EntityGraphs (A subset of fetch groups). Use JDO if full
fetch groups is an important factor for your design, otherwise either
• API experience : you may be more likely to find people with experience in JPA, but your
developers may already have experience with one API
There is a further comparison of JDO and JPA on technical grounds over at Apache JDO.
No. As part of SUN's capitulation above, they donated JDO to Apache to develop the technology
further. There have been the following revisions to the JDO specification;
• JDO2.1 adding on support for annotations, enums, and some JPA concepts.
• JDO2.2 adding on support for dynamic fetch groups, transaction isolation and cache control.
• JDO3.0 adding on MetaData/Enhancer APIs as well as query timeout/cancel support etc
In addition, JDO3.1 is reaching its conclusion, adding on support for more JDOQL methods, as well
as control over position of a column, size of a sequence etc.
Q: Will JPA replace JDO ?
It is very hard to see that happening since JPA provides nothing to cater for persistence of Java
objects to non-RDBMS datastores (LDAP, ODBMS, XML, ODF, Excel etc). It doesn't even provide
a complete definition of ORM, so cannot yet compete with JDO's ORM handling. Even in JPA2 (final
in late 2009) there are still basic ORM concepts that are not handled by JPA yet JDO standardises
them. JDO is still being developed, and while users require this technology then it will continue
to exist. DataNucleus will continue to support both APIs since there is a need for both in modern
enterprise applications despite what Oracle, IBM, et al try to impose on you.
Q: What differences are there between how JDO is developed and how JPA is developed ?
JPA is developed in isolation by an "expert group" though in JPA2.1 they have added a mailing list so
you can see their discussion (not that they'll reply to input necessarily). JDO is developed in public by
anybody interested in the technology. The tests to verify compliance with JPA are only available after
signing non-disclosure agreements with SUN and this process can take up to 3 months just to get the
test suite (if ever). The tests to verify compliance with JDO are freely downloadable and can be run by
users or developers. This means that anybody can check whether an implementation is compliant with
JDO, whereas the same is not true of JPA. DataNucleus runs the JDO3.x and JPA1 TCKs at frequent
intervals and we publish the results on our website. DataNucleus has been prevented from accessing
the JPA2 TCK (by Oracle and the JCP, documented in our blog).
Q: Why should I use JDO when JPA is supported by "large organisations" ?
By "large organisations" you presumably mean commercial organisations like Oracle, IBM, RedHat.
And they have their own vested interest in RDBMS technologies, or in selling application servers.
You should make your own decisions rather than just follow down the path you are shepherded
in by any commercial organisation. Your application will be supported by you not by them. The
technology you use should be the best for the job and what you feel most comfortable with. If you
feel more comfortable with JPA and it provides all that your application needs then use it. Similarly if
JDO provides what you need then you use that. For this reason DataNucleus provides support for both
specifications.
7 Development Process
.......................................................................................................................................
8 Compatibility
.......................................................................................................................................
datanucleus-core 4.0.4
datanucleus-api-jdo 4.0.4
datanucleus-api-jpa 4.0.5
datanucleus-api-rest 4.0.4
datanucleus-cassandra 4.0.4
datanucleus-excel 4.0.4
datanucleus-hbase 4.0.4
datanucleus-json 4.0.4
datanucleus-ldap 4.0.4
datanucleus-mongodb 4.0.4
datanucleus-neo4j 4.0.4
datanucleus-neodatis 4.0.4
datanucleus-odf 4.0.4
datanucleus-rdbms 4.0.5
datanucleus-xml 4.0.4
datanucleus-cache 4.0.4
datanucleus-geospatial 4.0.4
datanucleus-jodatime 4.0.4
datanucleus-guava 4.0.4
datanucleus-java8 4.0.4
datanucleus-jdo-query 4.0.4
datanucleus-jpa-query 4.0.4
datanucleus-maven-plugin 4.0.0-release
datanucleus-eclipse-plugin 4.0.0.release
Software Status
GraniteDS Fully compatible from GraniteDS 2.0+
Scala Fully compatible
Play Framework Fully compatible with version 2.0 or later. Version 1
of Play had a hardcoded Hibernate implementation
which was obviously a bad idea when the whole point
of having a persistence standard (JPA) is to allow
portability.
GWT Current versions of GWT (2+) ought to be able to
serialise/deserialise any detached JDO/JPA objects.
Earlier GWT versions had a problem with a bytecode
enhancement field of type Object[] and there was
a project GILEAD that attempted to handle this for
various persistence solutions (and in version 1.3 will
have specific support for DataNucleus, already in
Gilead SVN). Also look at this and this.
iReport v5 Fully compatible, but you could consider removing the
following iReport-5.0.0/ireport/modules/ext/commons-
dbcp-1.2.2.jar and iReport-5.0.0/ireport/modules/ext/
*hive* as they have been found by some to cause
conflicts.
SpringFramework Fully compatible since DataNucleus is a compliant
JDO/JPA implementation
9 Services
.......................................................................................................................................
1. Check the Documentation before anything else. The answer is usually there, either in a tutorial/
example, or in one of the many guides.
2. Look in the DataNucleus Log. This usually contains a lot of information that may answer the
issue. You can always configure the log to give more output.
3. Try a recent build to see if your version is out of date and the expected result is achieved with the
latest nightly builds.
4. Go to the Online Forum and ask. Always try to give as clear a description of the problem as
possible, together with your input data, and any associated log output. Please be aware that
we have very little time for this type of support and contributors to DataNucleus are more
likely to get any available free support
10 ORM Relationships
.......................................................................................................................................
PC 1-1 Bidirectional
PC 1-1 serialised
PC 1-1
CompoundIdentity
Unidirectional
PC 1-N
CompoundIdentity
Bidirectional
Interface 1-1 Unidirectional
Collection<PC> 1-N
CompoundIdentity
Unidirectional
Collection<PC> 1-N serialised
Collection
Collection<PC> 1-N JoinTable
Collection of
serialised elements
List<PC> 1-N ForeignKey
Unidirectional
Indexed List
List<PC> 1-N ForeignKey
Bidirectional
Indexed List
List<PC> 1-N JoinTable
Unidirectional
Indexed List
List<PC> 1-N JoinTable
Bidirectional
Indexed List
List<Non-PC> 1-N JoinTable
Indexed List
List<PC> 1-N ForeignKey
Unidirectional
Ordered List
List<PC> 1-N ForeignKey
Bidirectional
Ordered List
List<PC> 1-N JoinTable
Unidirectional
Ordered List
List<PC> 1-N JoinTable
Bidirectional
Ordered List
Map<PC, PC> 1-N JoinTable Map
11 Persistence Properties
.......................................................................................................................................
datanucleus.ConnectionURL
Description URL specifying the datastore to use for persistence.
Note that this will define the type of datastore
as well as the datastore itself. Please refer to the
datastores guides for the URL appropriate for the type
of datastore you're using.
Range of Values
datanucleus.ConnectionUserName
Description Username to use for connecting to the DB
Range of Values
datanucleus.ConnectionPassword
Description Password to use for connecting to the DB. See
datanucleus.ConnectionPasswordDecrypter for a way
of providing an encrypted password here
Range of Values
datanucleus.ConnectionDriverName
Description The name of the (JDBC) driver to use for the DB (for
RDBMS only).
Range of Values
datanucleus.ConnectionFactory
Description Instance of a connection factory for transactional
connections. This is an alternative to
datanucleus.ConnectionURL. For RDBMS, it must
be an instance of javax.sql.DataSource. See Data
Sources.
Range of Values
datanucleus.ConnectionFactory2
Description Instance of a connection factory for nontransactional
connections. This is an alternative to
datanucleus.ConnectionURL. For RDBMS, it must
be an instance of javax.sql.DataSource. See Data
Sources.
Range of Values
datanucleus.ConnectionFactoryName
Description The JNDI name for a connection factory for
transactional connections. For RBDMS, it must be
a JNDI name that points to a javax.sql.DataSource
object. See Data Sources.
Range of Values
datanucleus.ConnectionFactory2Name
Description The JNDI name for a connection factory for
nontransactional connections. For RBDMS, it must
be a JNDI name that points to a javax.sql.DataSource
object. See Data Sources.
Range of Values
datanucleus.ConnectionPasswordDecrypter
Description Name of a class that implements
org.datanucleus.store.connection.DecryptionProvider
and should only be specified if the password is
encrypted in the persistence properties
Range of Values
11.1.2 General
datanucleus.IgnoreCache
Description Whether to ignore the cache for queries. If the user
sets this to true then the query will evaluate in the
datastore, but the instances returned will be formed
from the datastore; this means that if an instance
has been modified and its datastore values match
the query then the instance returned will not be the
currently cached (updated) instance, instead an
instance formed using the datastore values.
Range of Values true | false
datanucleus.Multithreaded
Description Whether to run the PM/EM multithreaded. Note
that this is a hint only to try to allow thread-safe
operations on the PM/EM
Range of Values true | false
datanucleus.NontransactionalRead
Description Whether to allow nontransactional reads
Range of Values false | true
datanucleus.NontransactionalWrite
Description Whether to allow nontransactional writes
Range of Values false | true
datanucleus.Optimistic
datanucleus.RetainValues
Description Whether to suppress the clearing of values from
persistent instances on transaction completion. With
JDO this defaults to false, whereas for JPA it is true
Range of Values true | false
datanucleus.RestoreValues
Description Whether persistent object have transactional field
values restored when transaction rollback occurs.
Range of Values true | false
datanucleus.Mapping
Description Name for the ORM MetaData mapping files to use
with this PMF. For example if this is set to "mysql"
then the implementation looks for MetaData mapping
files called "{classname}-mysql.orm" or "package-
mysql.orm". If this is not specified then the JDO
implementation assumes that all is specified in the
JDO MetaData file.
Range of Values
datanucleus.mapping.Catalog
Description Name of the catalog to use by default for all classes
persisted using this PMF/EMF. This can be overridden
in the MetaData where required, and is optional.
DataNucleus will prefix all table names with this
catalog name if the RDBMS supports specification of
catalog names in DDL. RDBMS datastores only
Range of Values
datanucleus.mapping.Schema
datanucleus.tenantId
Description String id to use as a discriminator on all persistable
class tables to restrict data for the tenant using
this application instance (aka multi-tenancy via
discriminator). RDBMS, MongoDB datastores only
Range of Values
datanucleus.DetachAllOnCommit
Description Allows the user to select that when a transaction is
committed all objects enlisted in that transaction will
be automatically detached.
Range of Values true | false
datanucleus.detachAllOnRollback
Description Allows the user to select that when a transaction is
rolled back all objects enlisted in that transaction will
be automatically detached.
Range of Values true | false
datanucleus.CopyOnAttach
Description Whether, when attaching a detached object, we create
an attached copy or simply migrate the detached
object to attached state
Range of Values true | false
datanucleus.allowAttachOfTransient
Description When you call EM.merge with a transient object (with
PK fields set), if you enable this feature then it will first
check for existence of an object in the datastore with
the same identity and, if present, will merge into that
object (rather than just trying to persist a new object).
datanucleus.attachSameDatastore
Description When attaching an object DataNucleus by default
assumes that you're attaching to the same datastore
as you detached from. DataNucleus does though allow
you to attach to a different datastore (for things like
replication). Set this to false if you want to attach to a
different datastore to what you detached from
Range of Values true | false
datanucleus.detachAsWrapped
Description When detaching, any mutable second class objects
(Collections, Maps, Dates etc) are typically detached
as the basic form (so you can use them on client-
side of your application). This property allows you to
select to detach as wrapped objects. It only works with
"detachAllOnCommit" situations (not with detachCopy)
currently
Range of Values true | false
datanucleus.DetachOnClose
Description This allows the user to specify whether, when a PM/
EM is closed, that all objects in the L1 cache are
automatically detached. Users are recommended
to use the datanucleus.DetachAllOnCommit
wherever possible. This will not work in JCA mode.
Range of Values false | true
datanucleus.detachmentFields
Description When detaching you can control what happens to
loaded/unloaded fields of the FetchPlan. The default
for JDO is to load any unloaded fields of the current
FetchPlan before detaching. You can also unload
any loaded fields that are not in the current FetchPlan
(so you only get the fields you require) as well as a
combination of both options
Range of Values load-fields | unload-fields | load-unload-fields
datanucleus.maxFetchDepth
datanucleus.detachedState
Description Allows control over which mechanism to use to
determine the fields to be detached. By default
DataNucleus uses the defined "fetch-groups".
Obviously JPA1/JPA2 don't have that (although it is
an option with DataNucleus), so we also allow loaded
which will detach just the currently loaded fields,
and all which will detach all fields of the object ( be
careful with this option since it, when used with
maxFetchDepth of -1 will detach a whole object
graph!)
Range of Values fetch-groups | all | loaded
datanucleus.TransactionType
Description Type of transaction to use. If running under JavaSE
the default is RESOURCE_LOCAL, and if running
under JavaEE the default is JTA.
Range of Values RESOURCE_LOCAL | JTA
datanucleus.ServerTimeZoneID
Description Id of the TimeZone under which the datastore server
is running. If this is not specified or is set to null it is
assumed that the datastore server is running in the
same timezone as the JVM under which DataNucleus
is running.
Range of Values
datanucleus.PersistenceUnitName
Description Name of a persistence-unit to be found in a
persistence.xml file (under META-INF) that defines the
persistence properties to use and the classes to use
within the persistence process.
Range of Values
datanucleus.PersistenceUnitLoadClasses
Description Used when we have specified the persistence-
unit name for a PMF/EMF and where we want the
datastore "tables" for all classes of that persistence-
unit loading up into the StoreManager. Defaults to
false since some databases are slow so such an
operation would slow down the startup process.
Range of Values true | false
datanucleus.persistenceXmlFilename
Description URL name of the persistence.xml file that should be
used instead of using "META-INF/persistence.xml".
Range of Values
datanucleus.datastoreReadTimeout
Description The timeout to apply to all reads (millisecs). e.g by
query or by PM.getObjectById(). Only applies if the
underlying datastore supports it
Range of Values 0 | A positive value (MILLISECONDS)
datanucleus.datastoreWriteTimeout
Description The timeout to apply to all writes (millisecs). e.g by
makePersistent, or by an update. Only applies if the
underlying datastore supports it
Range of Values 0 | A positive value (MILLISECONDS)
datanucleus.singletonPMFForName
Description Whether to only allow a singleton PMF for a particular
name (the name can be either the name of the PMF in
jdoconfig.xml, or the name of the persistence-unit). If
a subsequent request is made for a PMF with a name
that already exists then a warning will be logged and
the original PMF returned.
Range of Values true | false
datanucleus.singletonEMFForName
datanucleus.allowListenerUpdateAfterInit
Description Whether you want to be able to add/remove listeners
on the JDO PMF after it is marked as not configurable
(when the first PM is created). The default matches
the JDO spec, not allowing changes to the listeners in
use.
Range of Values true | false
datanucleus.storeManagerType
Description Type of the StoreManager to use for this PMF/EMF.
This has typical values of "rdbms", "mongodb". If
it isnt specified then it falls back to trying to find
the StoreManager from the connection URL. The
associated DataNucleus plugin has to be in the
CLASSPATH when selecting this. When using data
sources (as usually done in a JavaEE container),
DataNucleus cannot find out the correct type
automatically and this option must be set.
Range of Values rdbms | mongodb | alternate StoreManager key
datanucleus.jmxType
Description Which JMX server to use when hooking into JMX.
Please refer to the Monitoring Guide
Range of Values default | mx4j
datanucleus.deletionPolicy
Description Allows the user to decide the policy when deleting
objects. The default is "JDO2" which firstly checks if
the field is dependent and if so deletes dependents,
and then for others will null any foreign keys out. The
problem with this option is that it takes no account
of whether the user has also defined <foreign-key>
elements, so we provide a "DataNucleus" mode
that does the dependent field part first and then if a
FK element is defined will leave it to the FK in the
datastore to perform any actions, and otherwise does
the nulling.
datanucleus.identityStringTranslatorType
Description You can allow identities input to pm.getObjectById(id)
be translated into valid JDO ids if there is a suitable
translator. See Identity String Translator Plugin
Range of Values
datanucleus.identityKeyTranslatorType
Description You can allow identities input to pm.getObjectById(cls,
key) be translated into valid JDO ids if there is a
suitable key translator. See Identity Key Translator
Plugin
Range of Values
datanucleus.datastoreIdentityType
Description Which "datastore-identity" class plugin to use to
represent datastore identities. Refer to Datastore
Identity extensions for details.
Range of Values datanucleus | kodo | xcalia | {user-supplied plugin}
datanucleus.executionContext.maxIdle
Description Specifies the maximum number of ExecutionContext
objects that are pooled ready for use
Range of Values 20 | integer value greater than 0
datanucleus.executionContext.reaperThread
Description Whether to start a reaper thread that continually
monitors the pool of ExecutionContext objects
and frees them off after they have surpassed their
expiration period
Range of Values false | true
datanucleus.objectProvider.className
Description Class name for the ObjectProvider to use when
managing object state. From v3.2.2
Range of Values org.datanucleus.state.JDOStateManager | {user-
provided class-name}
datanucleus.useImplementationCreator
Description Whether to allow use of the implementation-creator
(feature of JDO to dynamically create implementations
of persistent interfaces). Defaults to true for JDO, and
false for JPA
Range of Values true | false
datanucleus.manageRelationships
Description This allows the user control over whether DataNucleus
will try to manage bidirectional relations, correcting
the input objects so that all relations are consistent.
This process runs when flush()/commit() is called. JDO
defaults to true and JPA defaults to false You can set
it to false if you always set both sides of a relation
when persisting/updating.
Range of Values true | false
datanucleus.manageRelationshipsChecks
Description This allows the user control over whether DataNucleus
will make consistency checks on bidirectional
relations. If "datanucleus.managedRelationships"
is not selected then no checks are performed. If a
consistency check fails at flush()/commit() then a
JDOUserException is thrown. You can set it to false if
you want to omit all consistency checks.
Range of Values true | false
datanucleus.persistenceByReachabilityAtCommit
Description Whether to run the "persistence-by-reachability"
algorithm at commit() time. This means that objects
that were reachable at a call to makePersistent() but
that are no longer persistent will be removed from
persistence. For performance improvements, consider
turning this off.
Range of Values true | false
datanucleus.classLoaderResolverName
Description Name of a ClassLoaderResolver to use in class
loading. DataNucleus provides a default that loosely
follows the JDO specification for class loading. This
property allows the user to override this with their own
class better suited to their own loading requirements.
datanucleus.primaryClassLoader
Description Sets a primary classloader for situations where
a primary classloader is not accessible. This
ClassLoader is used when the class is not found in
the default ClassLoader search path. As example,
when the database driver is loaded by a different
ClassLoader not in the ClassLoader search path for
JDO or JPA specifications.
Range of Values instance of java.lang.ClassLoader
datanucleus.plugin.pluginRegistryClassName
Description Name of a class that acts as
registry for plug-ins. This defaults to
org.datanucleus.plugin.NonManagedPluginRegistry
(for when not using OSGi). If you are within
an OSGi environment you can set this to
org.datanucleus.plugin.OSGiPluginRegistry
Range of Values {fully-qualified class name}
datanucleus.plugin.pluginRegistryBundleCheck
Description Defines what happens when plugin bundles are found
and are duplicated
Range of Values EXCEPTION | LOG | NONE
datanucleus.plugin.allowUserBundles
Description Defines whether user-provided bundles providing
DataNucleus extensions will be registered. This is only
respected if used in a non-Eclipse OSGi environment.
Range of Values true | false
datanucleus.plugin.validatePlugins
Description Defines whether a validation step should be performed
checking for plugin dependencies etc. This is only
respected if used in a non-Eclipse OSGi environment.
Range of Values false | true
datanucleus.findObject.validateWhenCached
datanucleus.findObject.typeConversion
Description When calling PM.getObjectById(Class, Object) or
EM.find(Class, Object) the second argument really
ought to be the exact type of the primary-key field.
This property enables conversion of basic numeric
types (Long, Integer, Short) to the appropriate numeric
type (if the PK is a numeric type). Set this to false if
you want strict JPA compliance.
Range of Values true | false
datanucleus.schema.autoCreateAll
Description Whether to automatically generate any schema,
tables, columns, constraints that don't exist. Please
refer to the Schema Guide for more details.
Range of Values true | false
datanucleus.schema.autoCreateSchema
Description Whether to automatically generate any schema that
doesn't exist. This depends very much on whether the
datastore in question supports this operation. Please
refer to the Schema Guide for more details.
Range of Values true | false
datanucleus.schema.autoCreateTables
Description Whether to automatically generate any tables that
don't exist. Please refer to the Schema Guide for
more details.
Range of Values true | false
datanucleus.schema.autoCreateColumns
datanucleus.schema.autoCreateConstraints
Description Whether to automatically generate any constraints
that don't exist. Please refer to the Schema Guide for
more details.
Range of Values true | false
datanucleus.autoCreateWarnOnError
Description Whether to only log a warning when errors occur
during the auto-creation/validation process. Please
use with care since if the schema is incorrect
errors will likely come up later and this will
postpone those error checks til later, when it may
be too late!!
Range of Values true | false
datanucleus.schema.validateAll
Description Alias for defining
datanucleus.schema.validateTables,
datanucleus.schema.validateColumns and
datanucleus.schema.validateConstraints as all
true. Please refer to the Schema Guide for more
details.
Range of Values true | false
datanucleus.schema.validateTables
Description Whether to validate tables against the persistence
definition. Please refer to the Schema Guide for more
details.
Range of Values true | false
datanucleus.schema.validateColumns
Description Whether to validate columns against the persistence
definition. This refers to the column detail structure
and NOT to whether the column exists or not. Please
refer to the Schema Guide for more details.
Range of Values true | false
datanucleus.schema.validateConstraints
datanucleus.readOnlyDatastore
Description Whether the datastore is read-only or not (fixed in
structure and contents).
Range of Values true | false
datanucleus.readOnlyDatastoreAction
Description What happens when a datastore is read-only and an
object is attempted to be persisted.
Range of Values EXCEPTION | IGNORE
datanucleus.generateSchema.database.mode
Description Whether to perform any schema generation to the
database at startup. Will process the schema for all
classes that have metadata loaded at startup (i.e the
classes specified in a persistence-unit).
Range of Values create | drop | drop-and-create | none
datanucleus.generateSchema.scripts.mode
Description Whether to perform any schema generation into
scripts at startup. Will process the schema for all
classes that have metadata loaded at startup (i.e the
classes specified in a persistence-unit).
Range of Values create | drop | drop-and-create | none
datanucleus.generateSchema.scripts.create.target
Description Name of the script file to write to if doing a "create"
with the target as "scripts"
Range of Values datanucleus-schema-create.ddl | {filename}
datanucleus.generateSchema.scripts.drop.target
Description Name of the script file to write to if doing a "drop" with
the target as "scripts"
Range of Values datanucleus-schema-drop.ddl | {filename}
datanucleus.generateSchema.scripts.create.source
Description Name of a script file to run to create tables. Can be
absolute filename, or URL string
Range of Values {filename}
datanucleus.generateSchema.scripts.drop.source
Description Name of a script file to run to drop tables. Can be
absolute filename, or URL string
Range of Values {filename}
datanucleus.generateSchema.scripts.load
Description Name of a script file to run to load data into the
schema. Can be absolute filename, or URL string
Range of Values {filename}
datanucleus.identifierFactory
Description Name of the identifier factory to use when generating
table/column names etc (RDBMS datastores only).
See also the JDO RDBMS Identifier Guide.
Range of Values datanucleus1 | datanucleus2 | jpox | jpa | {user-
plugin-name}
datanucleus.identifier.namingFactory
Description Name of the identifier NamingFactory to use when
generating table/column names etc (non-RDBMS
datastores). Defaults to "datanucleus2" for JDO and
"jpa" for JPA usage.
Range of Values datanucleus2 | jpa | {user-plugin-name}
datanucleus.identifier.case
Description Which case to use in generated table/column identifier
names. See also the Datastore Identifier Guide.
RDBMS defaults to UPPERCASE. Cassandra defaults
to lowercase
Range of Values UPPERCASE | LowerCase | MixedCase
datanucleus.identifier.wordSeparator
Description Separator character(s) to use between words in
generated identifiers. Defaults to "_" (underscore)
datanucleus.identifier.tablePrefix
Description Prefix to be prepended to all generated table names (if
the identifier factory supports it)
datanucleus.identifier.tableSuffix
Description Suffix to be appended to all generated table names (if
the identifier factory supports it)
datanucleus.defaultInheritanceStrategy
Description How to choose the inheritance strategy default
for classes where no strategy has been specified.
With JDO2 this will be "new-table" for base classes
and "superclass-table" for subclasses. With
TABLE_PER_CLASS this will be "new-table" for all
classes.
Range of Values JDO2 | TABLE_PER_CLASS
datanucleus.store.allowReferencesWithNoImplementations
Description Whether we permit a reference field (1-1 relation) or
collection of references where there are no defined
implementations of the reference. False means that an
exception will be thrown
Range of Values true | false
datanucleus.transactionIsolation
Description Select the default transaction isolation level for ALL
PM/EM factories. Some databases do not support all
isolation levels, refer to your database documentation.
Please refer to the transaction guides for JDO and
JPA
Range of Values read-uncommitted | read-committed | repeatable-
read | serializable
datanucleus.SerializeRead
datanucleus.jtaLocator
Description Selects the locator to use when using JTA
transactions so that DataNucleus can find the JTA
TransactionManager. If this isn't specified and using
JTA transactions DataNucleus will search all available
locators which could have a performance impact. See
JTA Locator extension. If specifying "custom_jndi"
please also specify "datanucleus.jtaJndiLocation"
Range of Values jboss | jonas | jotm | oc4j | orion | resin | sap | sun |
weblogic | websphere | custom_jndi | alias of a JTA
transaction locator
datanucleus.jtaJndiLocation
Description Name of a JNDI location to find the JTA transaction
manager from (when using JTA transactions). This is
for the case where you know where it is located. If not
used DataNucleus will try certain well-known locations
Range of Values JNDI location
datanucleus.datastoreTransactionFlushLimit
Description For use when using datastore transactions and is the
limit on number of dirty objects before a flush to the
datastore will be performed.
Range of values 1 | positive integer
datanucleus.flush.mode
Description Sets when persistence operations are flushed to
the datastore. MANUAL means that operations will
be sent only on flush()/commit(). AUTO means that
operations will be sent immediately.
Range of values MANUAL | AUTO
datanucleus.flush.optimised
datanucleus.nontx.atomic
Description When a user invokes a nontransactional operation
they can choose for these changes to go straight
to the datastore (atomically) or to wait until either
the next transaction commit, or close of the PM/EM.
Disable this if you want operations to be processed
with the next real transaction. This defaults to true for
JDO, and false for JPA
Range of Values true | false
datanucleus.connectionPoolingType
Description This property allows you to utilise a 3rd party software
package for enabling connection pooling. When using
RDBMS you can select from DBCP, C3P0, Proxool,
BoneCP, or dbcp-builtin. You must have the 3rd party
jars in the CLASSPATH to use these options. Please
refer to the Connection Pooling guide for details.
Range of Values None | DBCP | C3P0 | Proxool | BoneCP | dbcp-builtin
| {others}
datanucleus.connectionPoolingType.nontx
Description This property allows you to utilise a 3rd party
software package for enabling connection pooling for
nontransactional connections using a DataNucleus
plugin. If you don't specify this value but do define the
above value then that is taken by default. Refer to the
above property for more details.
Range of Values None | DBCP | C3P0 | Proxool | BoneCP | "dbcp-
builtin" | {others}
datanucleus.connection.nontx.releaseAfterUse
datanucleus.connection.singleConnectionPerExecutionContext
Description With an ExecutionContext (PM/EM) we normally
allocate one connection for a transaction and close it
after the transaction, then a different connection for
nontransactional ops. This flag acts as a hint to the
store plugin to obtain and retain a single connection
throughout the lifetime of the PM/EM.
Range of Values true | false
datanucleus.connection.resourceType
Description Resource Type for connection ???
Range of Values JTA | RESOURCE_LOCAL
datanucleus.connection.resourceType2
Description Resource Type for connection 2
Range of Values JTA | RESOURCE_LOCAL
11.1.5 Caching
datanucleus.cache.collections
Description SCO collections can be used in 2 modes in
DataNucleus. You can allow DataNucleus to cache
the collections contents, or you can tell DataNucleus
to access the datastore for every access of the SCO
collection. The default is to use the cached collection.
Range of Values true | false
datanucleus.cache.collections.lazy
datanucleus.cache.level1.type
Description Name of the type of Level 1 cache to use. Defines the
backing map. See also Cache docs for JDO, and for
JPA
Range of Values soft | weak | strong | {your-plugin-name}
datanucleus.cache.level2.type
Description Name of the type of Level 2 Cache to use. Can be
used to interface with external caching products. Use
"none" to turn off L2 caching. See also Cache docs
for JDO, and for JPA
Range of Values none | soft | weak | coherence | ehcache |
ehcacheclassbased | cacheonix | oscache |
swarmcache | javax.cache | spymemcached |
xmemcached | {your-plugin-name}
datanucleus.cache.level2.mode
Description The mode of operation of the L2 cache,
deciding which entities are cached. The
default (UNSPECIFIED) is the same as
DISABLE_SELECTIVE. See also Cache docs for
JDO, and for JPA
Range of Values NONE | ALL | ENABLE_SELECTIVE |
DISABLE_SELECTIVE | UNSPECIFIED
datanucleus.cache.level2.storeMode
Description Whether to use the L2 cache for storing values (set
to "bypass" to not store within the context of the
operation)
Range of Values use | bypass
datanucleus.cache.level2.retrieveMode
Description Whether to use the L2 cache for retrieving values (set
to "bypass" to not retrieve from L2 cache within the
context of the operation, i.e go to the datastore)
datanucleus.cache.level2.updateMode
Description When the objects in the L2 cache should be updated.
Defaults to updating at commit AND when fields are
read from a datastore object
Range of Values commit-and-datastore-read | commit
datanucleus.cache.level2.cacheName
Description Name of the cache. This is for use with plugins such
as the Tangosol cache plugin for accessing the
particular cache. Please refer to the Cache Guide for
JDO or JPA
Range of Values your cache name
datanucleus.cache.level2.maxSize
Description Max size for the L2 cache (supported by weak,
soft, coherence, ehcache, ehcacheclassbased,
javax.cache)
Range of Values -1 | integer value
datanucleus.cache.level2.clearAtClose
Description Whether the close of the L2 cache (when the PMF/
EMF closes) should also clear out any objects from
the underlying cache mechanism. By default it will
clear objects out but if the user has configured an
external cache product and wants to share objects
across multiple PMF/EMFs then this can be set to
false.
Range of Values true | false
datanucleus.cache.level2.batchSize
Description When objects are added to the L2 cache at commit
they are typically batched. This property sets the max
size of the batch.
Range of Values 100 | integer value
datanucleus.cache.level2.timeout
Description Some caches (Cacheonix, javax.cache) allow
specification of an expiration time for objects in the
cache. This property is the timeout in milliseconds (will
be unset meaning use cache default).
datanucleus.cache.level2.readThrough
Description With javax.cache L2 caches you can configure the
cache to allow read-through
Range of Values true | false
datanucleus.cache.level2.writeThrough
Description With javax.cache L2 caches you can configure the
cache to allow write-through
Range of Values true | false
datanucleus.cache.level2.storeByValue
Description With javax.cache L2 caches you can configure the
cache to store by value (as opposed to by reference)
Range of Values true | false
datanucleus.cache.level2.statisticsEnabled
Description With javax.cache L2 caches you can configure the
cache to enable statistics gathering (accessible via
JMX)
Range of Values false | true
datanucleus.cache.queryCompilation.type
Description Type of cache to use for caching of generic query
compilations
Range of Values none | soft | weak | strong | {your-plugin-name}
datanucleus.cache.queryCompilationDatastore.type
Description Type of cache to use for caching of datastore query
compilations
Range of Values none | soft | weak | strong | {your-plugin-name}
datanucleus.cache.queryResults.type
Description Type of cache to use for caching query results.
Range of Values none | soft | weak | strong | javax.cache |
spymemcached | xmemcached | cacheonix | {your-
plugin-name}
datanucleus.cache.queryResults.cacheName
Description Name of cache for caching the query results.
Range of Values datanucleus-query | {your-name}
datanucleus.cache.queryResults.maxSize
Description Max size for the query results cache (supported by
weak, soft, strong)
Range of Values -1 | integer value
11.1.6 Validation
datanucleus.validation.mode
Description Determines whether the automatic lifecycle event
validation is in effect. Defaults to auto for JPA and
none for JDO
Range of Values auto | callback | none
datanucleus.validation.group.pre-persist
Description The classes to validation on pre-persist callback
Range of Values
datanucleus.validation.group.pre-update
Description The classes to validation on pre-update callback
Range of Values
datanucleus.validation.group.pre-remove
Description The classes to validation on pre-remove callback
Range of Values
datanucleus.validation.factory
Description The validation factory to use in validation
Range of Values
datanucleus.valuegeneration.transactionAttribute
Description Whether to use the PM connection or open a new
connection. Only used by value generators that
require a connection to the datastore.
Range of Values New | UsePM
datanucleus.valuegeneration.transactionIsolation
Description Select the default transaction isolation
level for identity generation. Must have
datanucleus.valuegeneration.transactionAttribute set
to New Some databases do not support all isolation
levels, refer to your database documentation. Please
refer to the transaction guides for JDO and JPA
Range of Values read-uncommitted | read-committed | repeatable-
read | serializable
datanucleus.valuegeneration.sequence.allocationSize
Description If using JDO3.0 still and not specifying the size of your
sequence, this acts as the default allocation size.
Range of Values 10 | (integer value)
datanucleus.valuegeneration.increment.allocationSize
Description Sets the default allocation size for any "increment"
value strategy. You can configure each member
strategy individually but they fall back to this value if
not set
Range of Values 10 | (integer value)
11.1.8 MetaData
datanucleus.metadata.jdoFileExtension
Description Suffix for JDO MetaData files. Provides the ability to
override the default suffix and also to have one PMF
with one suffix and another with a different suffix,
hence allowing differing persistence of the same
classes using different PMF's.
Range of values jdo | {file suffix}
datanucleus.metadata.ormFileExtension
datanucleus.metadata.jdoqueryFileExtension
Description Suffix for JDO Query MetaData files. Provides the
ability to override the default suffix and also to have
one PMF with one suffix and another with a different
suffix, hence allowing differing persistence of the same
classes using different PMF's.
Range of values jdoquery | {file suffix}
datanucleus.metadata.alwaysDetachable
Description Whether to treat all classes as detachable irrespective
of input metadata. See also "alwaysDetachable"
enhancer option.
Range of values false | true
datanucleus.metadata.xml.validate
Description Whether to validate the MetaData file(s) for XML
correctness (against the DTD) when parsing.
Range of values true | false
datanucleus.metadata.xml.namespaceAware
Description Whether to allow for XML namespaces in metadata
files. The vast majority of sane people should not need
this at all, but it's enabled by default to allow for those
that do (since v3.2.3)
Range of values true | false
datanucleus.metadata.allowXML
Description Whether to allow XML metadata. Turn this off if not
using any, for performance. From v3.0.4 onwards
Range of values true | false
datanucleus.metadata.allowAnnotations
Description Whether to allow annotations metadata. Turn this off if
not using any, for performance. From v3.0.4 onwards
datanucleus.metadata.allowLoadAtRuntime
Description Whether to allow load of metadata at runtime. This
is intended for the situation where you are handling
persistence of a persistence-unit and only want the
classes explicitly specified in the persistence-unit.
Range of values true | false
datanucleus.metadata.autoregistration
Description Whether to use the JDO auto-registration of metadata.
Turned on by default
Range of values true | false
datanucleus.metadata.supportORM
Description Whether to support "orm" mapping files. By default we
use what the datastore plugin supports. This can be
used to turn it off when the datastore supports it but
we dont plan on using it (for performance)
Range of values true | false
11.1.9 Auto-Start
datanucleus.autoStartMechanism
Description How to initialise DataNucleus at startup. This allows
DataNucleus to read in from some source the classes
that it was persisting for this data store the previous
time. "XML" stores the information in an XML file for
this purpose. "SchemaTable" (only for RDBMS) stores
a table in the RDBMS for this purpose. "Classes" looks
at the property datanucleus.autoStartClassNames
for a list of classes. "MetaData" looks at the property
datanucleus.autoStartMetaDataFiles for a list of
metadata files The other option is "None" (start from
scratch each time). Please refer to the Auto-Start
Mechanism Guide for more details. The default is
"None".
Range of Values None | XML | Classes | MetaData | SchemaTable
datanucleus.autoStartMechanismMode
datanucleus.autoStartMechanismXmlFile
Description Filename used for the XML file for AutoStart when
using "XML" Auto-Start Mechanism
datanucleus.autoStartClassNames
Description This property specifies a list of classes (comma-
separated) that are loaded at startup when using the
"Classes" Auto-Start Mechanism.
datanucleus.autoStartMetaDataFiles
Description This property specifies a list of metadata files (comma-
separated) that are loaded at startup when using the
"MetaData" Auto-Start Mechanism.
datanucleus.query.flushBeforeExecution
Description This property can enforce a flush to the datastore of
any outstanding changes just before executing all
queries. If using optimistic transactions any updates
are typically held back until flush/commit and so the
query would otherwise not take them into account.
Range of Values true | false
datanucleus.query.useFetchPlan
Description Whether to use the FetchPlan when executing a
JDOQL query. The default is to use it which means
that the relevant fields of the object will be retrieved.
This allows the option of just retrieving the identity
columns.
Range of Values true | false
datanucleus.query.compileOptimised
datanucleus.query.jdoql.allowAll
Description javax.jdo.query.JDOQL queries are allowed by JDO
only to run SELECT queries. This extension permits to
bypass this limitation so that DataNucleus extension
bulk "update" and bulk "delete" can be run.
Range of Values false | true
datanucleus.query.sql.allowAll
Description javax.jdo.query.SQL queries are allowed by JDO2
only to run SELECT queries. This extension permits
to bypass this limitation (so for example can execute
stored procedures).
Range of Values false | true
datanucleus.query.checkUnusedParameters
Description Whether to check for unused input parameters and
throw an exception if found. The JDO and JPA
specs require this check and is a good guide to
having misnamed a parameter name in the query for
example.
Range of Values true | false
datanucleus.rdbms.datastoreAdapterClassName
Description This property allows you to supply the class name
of the adapter to use for your datastore. The default
is not to specify this property and DataNucleus will
autodetect the datastore type and use its own internal
datastore adapter classes. This allows you to override
the default behaviour where there maybe is some
issue with the default adapter class. Applicable for
RDBMS only
Range of Values (valid class name on the CLASSPATH)
datanucleus.rdbms.useLegacyNativeValueStrategy
Description This property changes the process for deciding the
value strategy to use when the user has selected
"native"(JDO)/"auto"(JPA) to be like it was with version
3.0 and earlier, so using "increment" and "uuid-hex".
Applicable for RDBMS only
Range of Values true | false
datanucleus.rdbms.statementBatchLimit
Description Maximum number of statements that can be batched.
The default is 50 and also applies to delete of objects.
Please refer to the Statement Batching guide
Applicable for RDBMS only
Range of Values integer value (0 = no batching)
datanucleus.rdbms.checkExistTablesOrViews
Description Whether to check if the table/view exists. If false, it
disables the automatic generation of tables that don't
exist. Applicable for RDBMS only
Range of Values true | false
datanucleus.rdbms.initializeColumnInfo
Description Allows control over what column information is
initialised when a table is loaded for the first time.
By default info for all columns will be loaded.
Unfortunately some RDBMS are particularly poor at
returning this information so we allow reduced forms
to just load the primary key column info, or not to load
any. Applicable for RDBMS only
Range of Values ALL | PK | NONE
datanucleus.rdbms.classAdditionMaxRetries
Description The maximum number of retries when trying to find a
class to persist or when validating a class. Applicable
for RDBMS only
Range of Values 3 | A positive integer
datanucleus.rdbms.constraintCreateMode
Description How to determine the RDBMS constraints to be
created. DataNucleus will automatically add foreign-
keys/indices to handle all relationships, and will utilise
the specified MetaData foreign-key information. JDO2
will only use the information in the MetaData file(s).
Applicable for RDBMS only
datanucleus.rdbms.uniqueConstraints.mapInverse
Description Whether to add unique constraints to the element
table for a map inverse field. Possible values are true
or false. Applicable for RDBMS only
Range of values true | false
datanucleus.rdbms.discriminatorPerSubclassTable
Description Property that controls if only the base class where
the discriminator is defined will have a discriminator
column Applicable for RDBMS only
Range of values false | true
datanucleus.rdbms.stringDefaultLength
Description The default (max) length to use for all strings that
don't have their column length defined in MetaData.
Applicable for RDBMS only
Range of Values 255 | A valid length
datanucleus.rdbms.stringLengthExceededAction
Description Defines what happens when persisting a String field
and its length exceeds the length of the underlying
datastore column. The default is to throw an
Exception. The other option is to truncate the String
to the length of the datastore column. Applicable for
RDBMS only
Range of Values EXCEPTION | TRUNCATE
datanucleus.rdbms.persistEmptyStringAsNull
Description When persisting en empty string, should it be
persisted as null in the datastore. This is to allow for
datastores (Oracle) that dont differentiate between null
and empty string. If it is set to false and the datastore
doesnt differentiate then a special character will be
saved when storing an empty string. Applicable for
RDBMS only
Range of Values true | false
datanucleus.rdbms.query.fetchDirection
Description The direction in which the query results will be
navigated. Applicable for RDBMS only
datanucleus.rdbms.query.resultSetType
Description Type of ResultSet to create. Note 1) Not all JDBC
drivers accept all options. The values correspond
directly to the ResultSet options. Note 2) Not all
java.util.List operations are available for scrolling
result sets. An Exception is raised when unsupported
operations are invoked. Applicable for RDBMS only
Range of Values forward-only | scroll-sensitive | scroll-insensitive
datanucleus.rdbms.query.resultSetConcurrency
Description Whether the ResultSet is readonly or can be updated.
Not all JDBC drivers support all options. The
values correspond directly to the ResultSet options.
Applicable for RDBMS only
Range of Values read-only | updateable
datanucleus.rdbms.oracleNlsSortOrder
Description Sort order for Oracle String fields in queries (BINARY
disables native language sorting) Applicable for
RDBMS only
Range of Values LATIN | See Oracle documentation
datanucleus.rdbms.schemaTable.tableName
Description Name of the table to use when using auto-start
mechanism of "SchemaTable" Please refer to the
JDO Auto-Start guide Applicable for RDBMS only
Range of Values NUCLEUS_TABLES | Valid table name
datanucleus.rdbms.connectionProviderName
Description Name of the connection provider to use to allow
failover Please refer to the Failover guide Applicable
for RDBMS only
Range of Values PriorityList | Name of a provider
datanucleus.rdbms.connectionProviderFailOnError
Description Whether to fail if an error occurs, or try to continue and
log warnings Applicable for RDBMS only
Range of Values true | false
datanucleus.rdbms.dynamicSchemaUpdates
Description Whether to allow dynamic updates to the schema.
This means that upon each insert/update the types
of objects will be tested and any previously unknown
implementations of interfaces will be added to the
existing schema. Applicable for RDBMS only
Range of Values true | false
datanucleus.rdbms.omitDatabaseMetaDataGetColumns
Description Whether to bypass all calls to
DatabaseMetaData.getColumns(). This JDBC method
is called to get schema information, but on some
JDBC drivers (e.g Derby) it can take an inordinate
amout of time. Setting this to true means that your
datastore schema has to be correct and no checks will
be performed. Applicable for RDBMS only
Range of Values true | false
datanucleus.rdbms.sqlTableNamingStrategy
Description Name of the plugin to use for defining the names of
the aliases of tables in SQL statements. Applicable
for RDBMS only
Range of Values alpha-scheme | t-scheme
datanucleus.rdbms.tableColumnOrder
Description How we should order the columns in a table. The
default is to put the fields of the owning class first,
followed by superclasses, then subclasses. An
alternative is to start from the base superclass first,
working down to the owner, then the subclasses
Applicable for RDBMS only
Range of Values owner-first | superclass-first
datanucleus.rdbms.allowColumnReuse
Description This property allows you to reuse columns for more
than 1 field of a class. It is false by default to protect
the user from erroneously typing in a column name.
Additionally, if a column is reused, the user ought to
think about how to determine which field is written to
that column ... all reuse ought to imply the same value
in those fields so it doesn't matter which field is written
there, or retrieved from there. Applicable for RDBMS
only
Range of Values true | false
datanucleus.rdbms.statementLogging
Description How to log SQL statements. The default is to log
the statement and replace any parameters with the
value provided in angle brackets. Alternatively you
can log the statement with any parameters replaced
by just the values (no brackets). The final option is to
log the raw JDBC statement (with ? for parameters).
Applicable for RDBMS only
Range of Values values-in-brackets | values | jdbc
datanucleus.rdbms.fetchUnloadedAutomatically
Description If enabled will, upon a request to load a field, check
for any unloaded fields that are non-relation fields or
1-1/N-1 fields and will load them in the same SQL call.
Applicable for RDBMS only
Range of Values true | false
datanucleus.rdbms.adapter.informixUseSerialForIdentity
Description Whether we are using SERIAL for identity columns
(instead of SERIAL8). Applicable for RDBMS only
Range of Values true | false
datanucleus.cloud.storage.bucket
Description This is a mandatory property that allows you to supply
the bucket name to store your data. Applicable for
Google Storage, and AmazonS3
Range of Values Any valid string
datanucleus.hbase.enforceUniquenessInApplication
Description Setting this property to true means that when a new
object is persisted (and its identity is assigned), no
check will be made as to whether it exists in the
datastore and that the user takes responsibility for
such checks. Applicable for HBase
Range of Values true | false
datanucleus.cassandra.compression
Description Type of compression to use for the Cassandra cluster.
Applicable for Cassandra only
Range of Values none | snappy
datanucleus.cassandra.metrics
Description Whether metrics are enabled for the Cassandra
cluster. Applicable for Cassandra only
Range of Values true | false
datanucleus.cassandra.ssl
Description Whether SSL is enabled for the Cassandra cluster.
Applicable for Cassandra only
Range of Values true | false
datanucleus.cassandra.socket.readTimeoutMillis
Description Socket read timeout for the Cassandra cluster.
Applicable for Cassandra only
Range of Values
datanucleus.cassandra.socket.connectTimeoutMillis
Description Socket connect timeout for the Cassandra cluster.
Applicable for Cassandra only
Range of Values
12 Security
.......................................................................................................................................
Note that when you use -Djava.security.policy==... (double equals sign) you override the default
JVM security policy files, while if you use -Djava.security.policy=... (single equals sign), you append
the security policy file to any existing ones.
The following is a sample security policy file to be used with DataNucleus.
//jdo
permission javax.jdo.spi.JDOPermission "getMetadata";
permission javax.jdo.spi.JDOPermission "setStateManager";
//DataNucleus needs to read manifest files (read permission to location of MANIFEST.MF files)
permission java.io.FilePermission "${user.dir}${/}-", "read";
permission java.io.FilePermission "<<ALL FILES>>", "read";
13 Logging
.......................................................................................................................................
off a logging category. This is very useful in a production situation where maximum performance is
required.
To enable the DataNucleus log, you need to provide a Log4J configuration file when starting up your
application. This may be done for you if you are running within a JavaEE application server (check
your manual for details). If you are starting your application yourself, you would set a JVM parameter
as
-Dlog4j.configuration=file:log4j.properties
where log4j.properties is the name of your Log4J configuration file. Please note the "file:" prefix
to the file since a URL is expected. [When using java.util.logging you need to specify the system
property "java.util.logging.config.file"]
The Log4J configuration file is very simple in nature, and you typically define where the log goes to
(e.g to a file), and which logging level messages you want to see. Here's an example
# DataNucleus Categories
log4j.category.DataNucleus.JDO=INFO, A1
log4j.category.DataNucleus.Cache=INFO, A1
log4j.category.DataNucleus.MetaData=INFO, A1
log4j.category.DataNucleus.General=INFO, A1
log4j.category.DataNucleus.Transaction=INFO, A1
log4j.category.DataNucleus.Datastore=DEBUG, A1
log4j.category.DataNucleus.ValueGeneration=DEBUG, A1
log4j.category.DataNucleus.Enhancer=INFO, A1
log4j.category.DataNucleus.SchemaTool=INFO, A1
In this example, I am directing my log to a file ( datanucleus.log). I have defined a particular "pattern"
for the messages that appear in the log (to contain the date, level, category, and the message itself). In
addition I have assigned a level "threshold" for each of the DataNucleus categories. So in this case I
want to see all messages down to DEBUG level for the DataNucleus RDBMS persister.
Performance Tip : Turning OFF the logging, or at least down to ERROR level provides a significant
improvement in performance. With Log4J you do this via
log4j.category.DataNucleus=OFF
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
DataNucleus.General.level=fine
DataNucleus.JDO.level=fine
Please read the javadocs for java.util.logging for additional details on its configuration.
So you see the time of the log message, the level of the message (DEBUG, INFO, etc), the
category (DataNucleus.Datastore, etc), and the message itself. So, for example, if I had set the
DataNucleus.Datastore.Schema to DEBUG and all other categories to INFO I would see *all* DDL
statements sent to the database and very little else.
14 Datastore
.......................................................................................................................................
14.1 Datastores
The DataNucleus AccessPlatform is designed for flexibility to operate with any type of datastore.
We already support a very wide range of datastores and this will only increase in the future. In this
section you can find the specifics for particular supported datastores over and above what was already
addressed for JDO and JPA persistence.
If you have a requirement for persistence to some other datastore, then it would likely be easily
provided by creation of a DataNucleus StoreManager. Please contact us via the forum so that you can
provide this and contribute it back to the community.
15 Supported Features
.......................................................................................................................................
Feature
General
Features
Datastore
Identity
Application
Identity
Nondurable
Identity
[1]
Composite
Identity
Nontransactional
ACID
Transactions
Versioned
objects
Optimistic
Checks
Fetch
Plan
control
Native
Connection
access
(JDO)
Encryption
of
data
[7]
Backed
object
wrappers
[2]
Cascade
Persist
Cascade
Update
Cascade
Delete
Schema
Evolution
-
New
fields
[3]
Value
Generation
native(JDO)/
auto(JPA)
increment(JDO)/
table(JPA)
identity(JDO/
JPA)
sequence(JDO/
JPA)
uuid-
hex(JDO)
uuid-
string(JDO)
uuid
timestamp
timestamp-
value
max
O/R
Mapping
Indexes
Unique
Keys
Foreign
Keys
Primary
Keys
Inheritance(complete-
table)
[4]
Inheritance(new-
table)
Inheritance(subclass-
table)
Inheritance(superclass-
table)
Discriminators
Secondary
Tables
Join
Tables
Embedded
PC
Embedded
PC
stored
nested
( [8])
Embedded
Collection
Embedded
Map
Embedded
Array
Serialised
PC
Serialised
Collection
Serialised
Map
1-1
1-N
M-N
SchemaTool
Multitenancy
by
discriminator
Field
Types
Primitives,
Wrappers
java.lang.String
etc
java.lang.Enum
java.util.Date
etc
java.lang.Object
java.io.Serializable
java.util.Collection
java.util.Map
Arrays
Interfaces
Type
Converters
Type
Converter :
auto-
apply
Type
Converter :
multicolumn
Queries
JDOQL
evaluated
in
memory
JDOQL
evaluated
in
datastore
[5]
JDOQL
of
candidate
interface
JDOQL
Polymorphic
queries
JPQL
evaluated
in
memory
JPQL
evaluated
in
datastore
[5]
SQL
[6]
Stored
Procedures
JDOQL
Bulk
Update
JDOQL
Bulk
Delete
JPQL
Bulk
Update
JPQL
Bulk
Delete
[1]
means that datastore doesn't explicitly support inheritance but "complete-table" is the nearest to what
happens.
[5]
16 RDBMS
.......................................................................................................................................
DataNucleus supports persisting objects to RDBMS datastores (using the datanucleus-rdbms plugin).
It supports the vast majority of RDBMS products available today. DataNucleus communicates
with the RDBMS datastore using JDBC. RDBMS systems accept varying standards of SQL and so
DataNucleus will support particular RDBMS/JDBC combinations only, though clearly we try to
support as many as possible.
The jars required to use DataNucleus RDBMS persistence are datanucleus-core, datanucleus-api-jdo/
datanucleus-api-jpa, datanucleus-rdbms and JDBC driver.
There are tutorials available for use of DataNucleus with RDBMS for JDO and for JPA
By default when you create a PersistenceManagerFactory or EntityManagerFactory to connect to a
particular datastore DataNucleus will automatically detect the datastore adapter to use and will use
its own internal adapter for that type of datastore. If you find that either DataNucleus has incorrectly
detected the adapter to use, you can override the default behaviour using the persistence property
datanucleus.rdbms.datastoreAdapterClassName.
The following RDBMS have support built in to DataNucleus. Click on the one of interest to see
details of any provisos for its support, as well as the JDBC connection information
• MySQL/MariaDB
• PostgreSQL Database
• PostgreSQL+PostGIS Database
• HSQL DB
• H2 Database
• SQLite
• Apache Derby
• Microsoft SQLServer
• Sybase
• Oracle
• IBM DB2
• IBM Informix
• Firebird
• NuoDB
• SAPDB/MaxDB
• Virtuoso
• Pointbase
• Oracle TimesTen
• McKoi database
Note that if your RDBMS is not listed or currently supported you can easily write your own
Datastore Adapter for it raise an issue in DataNucleus JIRA when you have it working and attach a
patch to contribute it. Similarly if you are using an adapter that has some problem on your case you
could use the same plugin mechanism to override the non-working feature.
16.1.1 DB2
To specify DB2 as your datastore, you will need something like the following specifying (where
"mydb1" is the name of the database)
datanucleus.ConnectionDriverName=com.ibm.db2.jcc.DB2Driver
datanucleus.ConnectionURL=jdbc:db2://localhost:50002/mydb1
datanucleus.ConnectionUserName='username' (e.g db2inst1)
datanucleus.ConnectionPassword='password'
With DB2 Express-C v9.7 you need to have db2jcc.jar and db2jcc_license_cu.jar in the
CLASSPATH.
16.1.2 MySQL
MySQL and its more developed drop in replacement MariaDB are supported as an RDBMS datastore
by DataNucleus with the following provisos
• INNODB tables must be used since it is the only table type that allows foreign keys etc at the
moment. You can however define what type your table uses by setting the <class> extension
"mysql-engine-type" to be MyISAM or whatever for the class being persisted.
• JDOQL.isEmpty()/contains() will not work in MySQL 4.0 (or earlier) since the query uses
EXISTS and that is only available from MySQL 4.1
• MySQL on Windows MUST specify datanucleus.identifier.case as "LowerCase" since the
MySQL server stores all identifiers in lowercase BUT the mysql-connector-java JDBC driver
has a bug (in versions up to and including 3.1.10) where it claims that the MySQL server stores
things in mixed case when it doesnt
• MySQL 3.* will not work reliably with inheritance cases since DataNucleus requires UNION
and this doesn't exist in MySQL 3.*
• MySQL before version 4.1 will not work correctly on JDOQL Collection.size(), Map.size()
operations since this requires subqueries, which are not supported before MySQL 4.1.
• If you receive an error "Incorrect arguments to mysql_stmt_execute" then this is a bug in
MySQL and you need to update your JDBC URL to append "?useServerPrepStmts=false".
• MySQL throws away the milliseconds on a Date and so cannot be used reliably for Optimistic
locking using strategy "date-time" (use "version" instead)
• You can specify "BLOB", "CLOB" JDBC types when using MySQL with DataNucleus but
you must turn validation of columns OFF. This is because these types are not supported by the
MySQL JDBC driver and it returns them as LONGVARBINARY/LONGVARCHAR when
querying the column type
To specify MySQL as your datastore, you will need something like the following specifying
(replacing 'db-name' with name of your database etc)
datanucleus.ConnectionDriverName=com.mysql.jdbc.Driver
datanucleus.ConnectionURL=jdbc:mysql://'host':'port'/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
• MS SQL 2000 does not keep accuracy on datetime datatypes. This is an MS SQL 2000 issue. In
order to keep the accuracy when storing java.util.Date java types, use int datatype.
To specify MS SQL as your datastore, you will need something like the following specifying
(replacing 'db-name' with name of your database etc)
Microsoft SqlServer 2005 JDBC Driver (Recommended)
datanucleus.ConnectionDriverName=com.microsoft.sqlserver.jdbc.SQLServerDriver
datanucleus.ConnectionURL=jdbc:sqlserver://'host':'port';DatabaseName='db-name'
;SelectMethod=cursor
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
datanucleus.ConnectionDriverName=com.microsoft.jdbc.sqlserver.SQLServerDriver
datanucleus.ConnectionURL=jdbc:microsoft:sqlserver://'host':'port';DatabaseName='db-name'
;SelectMethod=cursor
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
16.1.4 Oracle
To specify Oracle as your datastore, you will need something like the following specifying (replacing
'db-name' with name of your database etc) ... you can also use 'oci' instead of 'thin' depending on your
driver.
datanucleus.ConnectionDriverName=oracle.jdbc.driver.OracleDriver
datanucleus.ConnectionURL=jdbc:oracle:thin:@'host':'port':'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
16.1.5 Sybase
To specify Sybase as your datastore, you will need something like the following specifying
(replacing 'db-name' with name of your database etc)
datanucleus.ConnectionDriverName=com.sybase.jdbc2.jdbc.SybDriver
datanucleus.ConnectionURL=jdbc:sybase:Tds:'host':'port'/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
16.1.6 HSQLDB
HSQLDB is supported as an RDBMS datastore by DataNucleus with the following proviso
• Use of batched statements is disabled since HSQLDB has a bug where it throws exceptions
"batch failed" (really informative). Still waiting for this to be fixed in HSQLDB
• Use of JDOQL/JPQL subqueries cannot be used where you want to refer back to the parent
query since HSQLDB up to and including version 1.8 don't support this.
To specify HSQL (1.x) as your datastore, you will need something like the following specifying
(replacing 'db-name' with name of your database etc)
datanucleus.ConnectionDriverName=org.hsqldb.jdbcDriver
datanucleus.ConnectionURL=jdbc:hsqldb:hsql://'host':'port'/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
16.1.7 H2
H2 is supported as an RDBMS datastore by DataNucleus
To specify H2 as your datastore, you will need something like the following specifying (replacing 'db-
name' with name of your database etc)
datanucleus.ConnectionDriverName=org.h2.Driver
datanucleus.ConnectionURL=jdbc:h2:'db-name'
datanucleus.ConnectionUserName=sa
datanucleus.ConnectionPassword=
16.1.8 Informix
Informix is supported as an RDBMS datastore by DataNucleus
To specify Informix as your datastore, you will need something like the following specifying
(replacing 'db-name' with name of your database etc)
datanucleus.ConnectionDriverName=com.informix.jdbc.IfxDriver
datanucleus.ConnectionURL=jdbc:informix-sqli://[{ip|host}:port][/dbname]:
INFORMIXSERVER=servername[;name=value[;name=value]...]
datanucleus.ConnectionUserName=informix
datanucleus.ConnectionPassword=password
e.g.
datanucleus.ConnectionDriverName=com.informix.jdbc.IfxDriver
datanucleus.ConnectionURL=jdbc:informix-sqli://192.168.254.129:9088:
informixserver=demo_on;database=buf_log_db
datanucleus.ConnectionUserName=informix
datanucleus.ConnectionPassword=password
Note that some database logging options in Informix do not allow changing autoCommit dinamically.
You need to rebuild the database to support it. To rebuild the database refer to Informix documention,
but as example, run $INFORMIXDIR\bin\dbaccess and execute the command "CREATE
DATABASE mydb WITH BUFFERED LOG".
INDEXOF: Informix 11.x does not have a function to search a string in another string. DataNucleus
defines a user defined function, DATANUCLEUS_STRPOS, which is automatically created on
startup. The SQL for the UDF function is:
let pos=-1;
for i=1+from to lenstr
if substr(str,i,lensearch)=search then
let pos=i;
exit for;
end if;
end for;
return pos;
end function;
16.1.9 PostgreSQL
To specify PostgreSQL as your datastore, you will need something like the following specifying
(replacing 'db-name' with name of your database etc)
datanucleus.ConnectionDriverName=org.postgresql.Driver
datanucleus.ConnectionURL=jdbc:postgresql://'host':'port'/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
datanucleus.ConnectionDriverName=org.postgresql.Driver
datanucleus.ConnectionURL=jdbc:postgresql://'host':'port'/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
For Oracle's JGeometry you will need something like the following specifying (replacing 'db-name'
with name of your database etc)
datanucleus.ConnectionDriverName=org.postgresql.Driver
datanucleus.ConnectionURL=jdbc:postgres_jgeom://'host':'port'/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
For the JTS (Java Topology Suite) geometries you will need something like the following specifying
(replacing 'db-name' with name of your database etc)
datanucleus.ConnectionDriverName=org.postgresql.Driver
datanucleus.ConnectionURL=jdbc:postgres_jts://'host':'port'/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
datanucleus.ConnectionDriverName=org.apache.derby.jdbc.EmbeddedDriver
datanucleus.ConnectionURL=jdbc:derby:'db-name';create=true
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
Above settings are used together with the Apache Derby in embedded mode. The below settings are
used in network mode, where the default port number is 1527.
datanucleus.ConnectionDriverName=org.apache.derby.jdbc.ClientDriver
datanucleus.ConnectionURL=jdbc:derby://'hostname':'portnumber'/'db-name';create=true
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
org.apache.derby.jdbc.ClientDriver
ASCII: Derby 10.1 does not have a function to convert a char into ascii code. DataNucleus needs
such function to converts chars to int values when performing queries converting chars to ints.
DataNucleus defines a user defined function, DataNucleus_ASCII, which is automatically created on
startup. The SQL for the UDF function is:
16.1.12 Firebird
Firebird is supported as an RDBMS datastore by DataNucleus with the proviso that
• Auto-table creation is severely limited with Firebird. In Firebird, DDL statements are not auto-
committed and are executed at the end of a transaction, after any DML statements. This makes
"on the fly" table creation in the middle of a DML transaction not work. You must make sure
that "autoStartMechanism" is NOT set to "SchemaTable" since this will use DML. You must
also make sure that nobody else is connected to the database at the same time. Don't ask us why
such limitations are in a RDBMS, but then it was you that chose to use it ;-)
To specify Firebird as your datastore, you will need something like the following specifying
(replacing 'db-name' with filename of your database etc)
datanucleus.ConnectionDriverName=org.firebirdsql.jdbc.FBDriver
datanucleus.ConnectionURL=jdbc:firebirdsql://localhost/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
16.1.13 NuoDB
To specify NuoDB as your datastore, you will need something like the following specifying
(replacing 'db-name' with filename of your database etc)
datanucleus.ConnectionDriverName=com.nuodb.jdbc.Driver
datanucleus.ConnectionURL=jdbc:com.nuodb://localhost/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
datanucleus.Schema={my-schema-name}
16.1.14 SAPDB/MaxDB
To specify SAPDB/MaxDB as your datastore, you will need something like the following specifying
(replacing 'db-name' with filename of your database etc)
datanucleus.ConnectionDriverName=com.sap.dbtech.jdbc.DriverSapDB
datanucleus.ConnectionURL=jdbc:sapdb://localhost/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
16.1.15 SQLite
SQLite is supported as an RDBMS datastore by DataNucleus with the proviso that
datanucleus.ConnectionDriverName=org.sqlite.JDBC
datanucleus.ConnectionURL=jdbc:sqlite:'db-name'
datanucleus.ConnectionUserName=
datanucleus.ConnectionPassword=
16.1.16 Virtuoso
To specify Virtuoso as your datastore, you will need something like the following specifying
(replacing 'db-name' with filename of your database etc)
datanucleus.ConnectionDriverName=virtuoso.jdbc.Driver
datanucleus.ConnectionURL=jdbc:virtuoso://127.0.0.1/{dbname}
datanucleus.ConnectionUserName=
datanucleus.ConnectionPassword=
16.1.17 Pointbase
To specify Pointbase as your datastore, you will need something like the following specifying
(replacing 'db-name' with filename of your database etc)
datanucleus.ConnectionDriverName=com.pointbase.jdbc.jdbcUniversalDriver
datanucleus.ConnectionURL=jdbc:pointbase://127.0.0.1/{dbname}
datanucleus.ConnectionUserName=
datanucleus.ConnectionPassword=
16.1.18 McKoi
McKoi is supported as an RDBMS datastore by DataNucleus with the following proviso
datanucleus.ConnectionDriverName=com.mckoi.JDBCDriver
datanucleus.ConnectionURL=jdbc:mckoi://'host':'port'/'db-name'
datanucleus.ConnectionUserName='user-name'
datanucleus.ConnectionPassword='password'
org.postgis.MultiPolygon datanucleus-
[1] geospatial
org.postgis.Point datanucleus-
[1] geospatial
org.postgis.Polygon datanucleus-
[1] geospatial
org.postgis.PGbox2d datanucleus-
[1] geospatial
org.postgis.PGbox3d datanucleus-
[1] geospatial
• Dirty check mechanism is limited to immutable mode, it means, if you change a field of one
of these spatial objects, you must reassign it to the owner object field to make sure changes are
propagated to the database.
The implementation of these spatial types follows the OGC Simple Feature specification, but adds
further types where the datastores support them.
PostgreSQL with
Geometry Libraries MySQL [1] Oracle [2] [4] PostGIS [3] [4] [5]
Oracle's JGeometry
jgeom2mysql jgeom2oracle
Java Topology Suite
(JTS)
jts2mysql jts2oracle jts2postgis
PostGIS JDBC
Geometries
pg2mysql pg2postgis
• [1] - MySQL doesn't support 3-dimensional geometries. Trying to persist them anyway results
in undefined behaviour, there may be an exception thrown or the z-ordinate might just get
stripped.
• [2] - Oracle Spatial supports additional data types like circles and curves that are not defined in
the OGC SF specification. Any attempt to read or persist one of those data types, if you're not
using jgeom2oracle, will result in failure!
• [3] - PostGIS added support for curves in version 1.2.0, but at the moment the JDBC driver
doesn't support them yet. Any attempt to read curves geometries will result in failure, for every
mapping scenario!
• [4] - Both PostGIS and Oracle have a system to add user data to specific points of a geometry. In
PostGIS these types are called measure types and the z-coordinate of every 2d-point can be used
to store arbitrary (numeric) data of double precision associated with that point. In Oracle this
user data is called LRS. DataNucleus-Spatial tries to handle these types as gracefully as possible.
But the recommendation is to not use them, unless you have a mapping scenario that is known to
support them, i.e. pg2postgis for PostGIS and jgeom2oracle for Oracle.
• [5] - PostGIS supports two additional types called box2d and box3d, that are not defined in
OGC SF. There are only mappings available for these types in pg2postgis, any attempt to read or
persist one of those data types in another mapping scenario will result in failure!
Geometry
Library Java Type MySQL Oracle PostGIS
JGeometry oracle.spatial.geometry.JGeometry
geometry SDO_GEOMETRY
JTS com.vividsolutions.jts.geom.Geometry
geometry geometry
PostGIS-JDBC org.postgis.GeometryCollection
geometry geometry
PostGIS-JDBC org.postgis.LinearRing
geometry geometry
PostGIS-JDBC org.postgis.LineString
geometry geometry
PostGIS-JDBC org.postgis.MultiLineString
geometry geometry
PostGIS-JDBC org.postgis.MultiPoint
geometry geometry
PostGIS-JDBC org.postgis.MultiPolygon
geometry geometry
PostGIS-JDBC org.postgis.Point
geometry geometry
PostGIS-JDBC org.postgis.Polygon
geometry geometry
PostGIS-JDBC org.postgis.PGbox2d
box2d
PostGIS-JDBC org.postgis.PGbox3d
box3d
17.1.3 Metadata
DataNucleus-Spatial has defined some metadata extensions that can be used to give additional
information about the geometry types in use. The position of these tags in the meta-data determines
their scope. If you use them inside a <field>-tag the values are only used for that field specifically, if
you use them inside the <package>-tag the values are in effect for all (geometry) fields of all classes
inside that package, etc.
<package name="org.datanucleus.samples.jtsgeometry">
<extension vendor-name="datanucleus" key="spatial-dimension" value="2"/> [1]
<extension vendor-name="datanucleus" key="spatial-srid" value="4326"/> [1]
• [1] - The srid & dimension values are used in various places. One of them is schema creation,
when using PostGIS, another is when you query the SpatialHelper.
• [2] - Every JTS geometry object can have a user data object attached to it. The default behaviour
is to serialize that object and store it in a separate column in the database. If for some reason
this isn't desired, the mapping extension can be used with value "no-mapping" and DataNucleus-
Spatial will ignore the user data objects.
• [3] - If you want to use measure types in PostGIS you have to define that using the postgis-
hasMeasure extension.
17.1.5 Dependencies
Depending on the mapping scenario you want to use, there is a different set of JARs that need to be in
your classpath.
18 Datastore Types
.......................................................................................................................................
This table shows the Java types we saw earlier and whether they can be queried using JDOQL
queries, and what JDBC types can be used to store them in your RDBMS datastore. Not all RDBMS
datastores support all of these options. While DataNucleus always tries to provide a complete list
sometimes this is impossible due to limitations in the underlying JDBC driver
Number
Java Type Columns Queryable JDBC Type(s)
boolean 1 BIT, CHAR ('Y','N'),
BOOLEAN, TINYINT,
SMALLINT, NUMERIC
byte 1 TINYINT, SMALLINT,
NUMERIC
char 1 CHAR, INTEGER,
NUMERIC
double 1 DOUBLE, DECIMAL,
FLOAT
float 1 FLOAT, REAL, DOUBLE,
DECIMAL
int 1 INTEGER, BIGINT,
NUMERIC
java.lang.Byte[] 1 LONGVARBINARY,
BLOB
[5]
java.lang.Character[] 1 LONGVARBINARY,
BLOB
[5]
java.lang.Double[] 1 LONGVARBINARY,
BLOB
[5]
java.lang.Float[] 1 LONGVARBINARY,
BLOB
[5]
java.lang.Integer[] 1 LONGVARBINARY,
BLOB
[5]
java.lang.Long[] 1 LONGVARBINARY,
BLOB
[5]
java.lang.Short[] 1 LONGVARBINARY,
BLOB
[5]
java.lang.Number 1
java.lang.Object 1 LONGVARBINARY,
BLOB
java.lang.String [8] 1 VARCHAR, CHAR,
LONGVARCHAR, CLOB,
BLOB, DATALINK [6],
UNIQUEIDENTIFIER [7],
XMLTYPE [9]
java.lang.StringBuffer [8] 1 VARCHAR, CHAR,
LONGVARCHAR, CLOB,
BLOB, DATALINK [6],
UNIQUEIDENTIFIER [7],
XMLTYPE [9]
java.lang.String[] 1 LONGVARBINARY,
BLOB
[5]
java.lang.Enum 1 LONGVARBINARY,
BLOB, VARCHAR,
INTEGER
java.lang.Enum[] 1 LONGVARBINARY,
BLOB
[5]
java.math.BigDecimal 1 DECIMAL, NUMERIC
java.math.BigDecimal[] 1 LONGVARBINARY,
BLOB
[5]
java.math.BigInteger[] 1 LONGVARBINARY,
BLOB
[5]
java.sql.Date 1 DATE, TIMESTAMP
java.sql.Timestamp 1 TIMESTAMP
java.util.ArrayList 0
java.util.BitSet 0 LONGVARBINARY,
BLOB
java.util.Calendar [3] 1 or 2 INTEGER, VARCHAR,
CHAR
java.util.Collection 0
java.util.HashSet 0
java.util.Hashtable 0
java.util.LinkedHashMap 0
java.util.LinkedHashSet 0
java.util.LinkedList 0
java.util.List 0
java.util.Properties 0
java.util.PriorityQueue 0
java.util.Queue 0
java.util.Set 0
java.util.SortedMap 0
java.util.SortedSet 0
java.util.Stack 0
java.util.TreeSet 0
java.awt.image.BufferedImage
1 LONGVARBINARY,
[4] BLOB
java.net.URI [8] 1 VARCHAR, CHAR,
LONGVARCHAR, CLOB,
BLOB, DATALINK [7],
UNIQUEIDENTIFIER [8],
XMLTYPE [10]
java.net.URL [8] 1 VARCHAR, CHAR,
LONGVARCHAR, CLOB,
BLOB, DATALINK [7],
UNIQUEIDENTIFIER [8],
XMLTYPE [10]
java.io.Serializable 1 LONGVARBINARY,
BLOB
javax.jdo.spi.PersistenceCapable
1 [embedded]
javax.jdo.spi.PersistenceCapable[]
1
[5]
• [1] - java.awt.Color - stored in 4 columns (red, green, blue, alpha). ColorSpace is not persisted.
• [2] - java.awt.Point - stored in 2 columns (x and y).
• [3] - java.util.Calendar - stored in 2 columns (milliseconds and timezone).
• [4] - java.awt.image.BufferedImage is stored using JPG image format
• [5] - Array types are queryable if not serialised, but stored to many rows
• [6] - DATALINK JDBC type supported on DB2 only. Uses the SQL function
DLURLCOMPLETEONLY to fetch from the datastore. You can override this using the select-
function extension. See the JDO MetaData reference.
• [7] - UNIQUEIDENTIFIER JDBC type supported on MSSQL only.
• [8] - Oracle treats an empty string as the same as NULL. To workaround this limitation
DataNucleus replaces the empty string with the character \u0001.
• [9] - XMLTYPE JDBC type supported on Oracle only, and is included in the "datanucleus-
rdbms" plugin.
If you need to extend the provided DataNucleus capabilities in terms of its datastore types support you
can utilise a plugin point.
BIGINT
BIT
BLOB
BOOLEAN
CHAR
CLOB
DATE
DECIMAL
DISTINCT
DOUBLE
FLOAT
INTEGER
JAVA_OBJECT
LONGVARBINARY
LONGVARCHAR
NULL
NUMERIC
REF
SMALLINT
TIME
TIMESTAMP
TINYINT
VARBINARY
VARCHAR
19 Failover
.......................................................................................................................................
19.1.1 Sequoia
Sequoia is a transparent middleware solution offering clustering, load balancing and failover services
for any database. Sequoia is the continuation of the C-JDBC project. The database is distributed and
replicated among several nodes and Sequoia balances the queries among these nodes. Sequoia handles
node and network failures with transparent failover. It also provides support for hot recovery, online
maintenance operations and online upgrades.
Sequoia can be used with DataNucleus by just providing the Sequoia datastore URLs as input to
DataNucleus. There is a problem outstanding in Sequoia itself in that its JDBC driver doesnt
provide DataNucleus with the correct major/minor versions of the underlying datastore. Until
Sequoia fix this issue, use of Sequoia will be unreliable
DataNucleus has the capability to switch to between DataSources upon failure of one while obtaining
a datastore connection. The failover mechanism is useful for applications with multiple database
nodes when the data is actually replicated/synchronized by the underlying database. There are 2 things
to be aware of before utilising this functionality.
• DataNucleus doesn't replicate changes to all database nodes, and for this reason, this feature is
suggested to be used only for reading objects or if the database is capable to replicate the changes
to all nodes.
• If a connection breaks while in use the failover mechanism will not handle it, thus the user
application must take care of restarting the transaction and execute the operations.
Several failover algorithm are allowed to be used, one at time, as for example round-robin,
ordered list or random. The default algorithm, ordered list, is described below and is provided by
DataNucleus. You can also implement and plug your own algorithm. See Connection Provider.
To use failover, each datastore connection must be provided through DataSources. The
datanucleus.ConnectionFactoryName property must be declared with a list of JNDI names pointing to
DataSources, in the form of <JNDINAME> [,<JNDINAME>]. See the example:
datanucleus.ConnectionFactoryName=JNDINAME1,JNDINAME2
Each time DataNucleus needs to obtain a connection to the datastore, it takes the first DataSource,
the Master, and tries, on failure to obtain the connection goes to the next on the list until it obtains a
connection to the datastore or the end of the list is reached.
The first JNDI name in the datanucleus.ConnectionFactoryName property is the Master DataSource
and the following JNDI names are the Slave DataSources.
20 Queries
.......................................................................................................................................
query.addExtension("datanucleus.rdbms.query.resultSetType", "scroll-insensitive");
query.setHint("datanucleus.rdbms.query.resultSetType", "scroll-insensitive");
The default is forward-only. The benefit of the other two is that the result set will be scrollable and
hence objects will only be read in to memory when accessed. So if you have a large result set you
should set this to one of the scrollable values.
query.addExtension("datanucleus.query.resultCacheType", "weak");
query.setHint("datanucleus.query.resultCacheType", "weak");
If you have a large result set you clearly don't want to instantiate all objects since this would hit the
memory footprint of your application. To get the number of results many JDBC drivers will load all
rows of the result set. This is to be avoided so DataNucleus provides control over the mechanism
for getting the size of results. The persistence property datanucleus.query.resultSizeMethod has a
default of last (which means navigate to the last object - hence hitting the JDBC driver problem). If
you set this to count then it will use a simple "count()" query to get the size.
To do this on a per query basis for JDO you would do
query.addExtension("datanucleus.query.resultSizeMethod", "count");
query.setHint("datanucleus.query.resultSizeMethod", "count");
When a transaction is committed by default all remaining results for a query are loaded so that the
query is usable thereafter. With a large result set you clearly don't want this to happen. So in this case
you should set the extension datanucleus.query.loadResultsAtCommit to false.
To do this on a per query basis for JDO you would do
query.addExtension("datanucleus.query.loadResultsAtCommit", "false");
query.setHint("datanucleus.query.loadResultsAtCommit", "false");
DataNucleus provides a useful extension allowing control over the ResultSet's that are created by
queries. You have at your convenience some properties that give you the power to control whether the
result set is read only, whether it can be read forward only, the direction of fetching etc.
To do this on a per query basis for JDO you would do
query.addExtension("datanucleus.rdbms.query.fetchDirection", "forward");
query.addExtension("datanucleus.rdbms.query.resultSetConcurrency", "read-only");
query.setHint("datanucleus.rdbms.query.fetchDirection", "forward");
query.setHint("datanucleus.rdbms.query.resultSetConcurrency", "read-only");
Alternatively you can specify these as persistence properties so that they apply to all queries for that
PMF/EMF. Again, the properties are
Note that we add the contains first that binds the variable "b1" to the element table, and then add
the condition on the variable. The order is important here. If we instead had put the condition on the
variable first we would have had to do a CROSS JOIN to the variable table and then try to repair the
situation and change it to INNER JOIN if possible. In this case the generated SQL will be like
SELECT `A0`.`ID`
FROM `A` `A0`
INNER JOIN `B` `B0` ON `A0`.ID = `B`.ELEMENT
WHERE `B0`.NAME = 'Jones'
JPQL:
SELECT Object(P) FROM mydomain.Person P INNER JOIN P.bestFriend AS B
SQL:
SELECT P.ID
FROM PERSON P INNER JOIN PERSON B ON B.ID = P.BESTFRIEND_ID
With the JPQL MEMBER OF syntax this is typically converted into an EXISTS query.
JPQL:
SELECT DISTINCT Object(p) FROM mydomain.Person p WHERE :param MEMBER OF p.friends
SQL:
SELECT DISTINCT P.ID FROM PERSON P
WHERE EXISTS (
SELECT 1 FROM PERSON_FRIENDS P_FRIENDS, PERSON P_FRIENDS_1
WHERE P_FRIENDS.PERSON_ID = P.ID
AND P_FRIENDS_1.GLOBAL_ID = P_FRIENDS.FRIEND_ID
AND 101 = P_FRIENDS_1.ID)
When querying spatial data you can make use of a set of spatial methods on the various Java
geometry types. The list contains all of the functions detailed in Section 3.2 of the OGC Simple
Features specification. Additionally DataNucleus provides some commonly required methods like
bounding box test and datastore specific functions. The following tables list all available functions as
well as information about which RDBMS implement them. An entry in the "Result" column indicates,
whether the funcion may be used in the result part of a JDOQL query.
Functions for Constructing a Geometry Value given its Well-known Text Representation (OGC
SF 3.2.6)
Oracle
Method Description Specification Result [1] PostGIS MySQL Spatial
Spatial.geomFromText(String,
Construct a OGC SF
Integer) Geometry
value given
its well-
known textual
representation.
Spatial.pointFromText(String,
Construct a OGC SF
Integer) Point.
Spatial.lineFromText(String,
Construct a OGC SF
Integer) LineString.
Spatial.polyFromText(String,
Construct a OGC SF
Integer) Polygon.
Spatial.mPointFromText(String,
Construct a OGC SF
Integer) MultiPoint.
Spatial.mLineFromText(String,
Construct a OGC SF
Integer) MultiLineString.
Spatial.mPolyFromText(String,
Construct a OGC SF
Integer) MultiPolygon.
Spatial.geomCollFromText(String,
Construct a OGC SF
Integer) GeometryCollection.
[1] These functions can't be used in the return part because it's not possible to determine the return
type from the parameters.
Functions for Constructing a Geometry Value given its Well-known Binary Representation
(OGC SF 3.2.7)
Oracle
Method Description Specification Result [1] PostGIS MySQL Spatial
Spatial.geomFromWKB(String,
Construct a OGC SF
Integer) Geometry
value given
its well-
known binary
representation.
Spatial.pointFromWKB(String,
Construct a OGC SF
Integer) Point.
Spatial.lineFromWKB(String,
Construct a OGC SF
Integer) LineString.
Spatial.polyFromWKB(String,
Construct a OGC SF
Integer) Polygon.
Spatial.mPointFromWKB(String,
Construct a OGC SF
Integer) MultiPoint.
Spatial.mLineFromWKB(String,
Construct a OGC SF
Integer) MultiLineString.
Spatial.mPolyFromWKB(String,
Construct a OGC SF
Integer) MultiPolygon.
Spatial.geomCollFromWKB(String,
Construct a OGC SF
Integer) GeometryCollection.
[1] These functions can't be used in the return part because it's not possible to determine the return
type from the parameters.
Functions on Type Geometry
(OGC SF 3.2.10)
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.dimension(Geometry)
Returns the OGC SF
dimension
of the
Geometry.
Spatial.geometryType(Geometry)
Returns the OGC SF
name of the
instantiable
subtype of
Geometry.
Spatial.asText(Geometry)
Returns OGC SF
the well-
known textual
representation.
Spatial.asBinary(Geometry)
Returns OGC SF
the well-
known binary
representation.
Spatial.srid(Geometry)
Returns OGC SF
the Spatial
Reference
System
ID for this
Geometry.
Spatial.isEmpty(Geometry)
TRUE if this OGC SF
Geometry
corresponds [1] [2]
to the empty
set.
Spatial.isSimple(Geometry)
TRUE if this OGC SF
Geometry is
simple, as [1] [2]
defined in the
Geometry
Model.
Spatial.boundary(Geometry)
Returns a OGC SF
Geometry
that is the [2]
combinatorial
boundary
of the
Geometry.
Spatial.envelope(Geometry)
Returns the OGC SF
rectangle
bounding
Geometry as
a Polygon.
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.x(Point) Returns the x- OGC SF
coordinate of
the Point as a
Double.
Spatial.y(Point) Returns the y- OGC SF
coordinate of
the Point as a
Double.
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.startPoint(Curve))
Returns the OGC SF
first point of
the Curve.
Spatial.endPoint(Curve))
Returns the OGC SF
last point of
the Curve.
Spatial.isRing(Curve)
Returns OGC SF
TRUE if
Curve is [1] [2]
closed and
simple. .
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.isClosed(Curve)
Returns OGC SF
Spatial.isClosed(MultiCurve)
TRUE if
Curve is [1]
closed, i.e., if
StartPoint(Curve)
=
EndPoint(Curve).
Spatial.length(Curve)
Returns the OGC SF
Spatial.length(MultiCurve)
length of the
Curve.
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.numPoints(LineString)
Returns the OGC SF
number of
points in the
LineString.
Spatial.pointN(LineString,
Returns Point OGC SF
Integer) n.
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.centroid(Surface)
Returns the OGC SF
centroid(MultiSurface)
centroid of
Surface, [1]
which may lie
outside of it.
Spatial.pointOnSurface(Surface)
Returns OGC SF
pointOnSurface(MultiSurface)
a Point
guaranteed [1]
to lie on the
surface.
Spatial.area(Surface)
Returns OGC SF
area(MultiSurface)
the area of
Surface.
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.exteriorRing(Polygon)
Returns the OGC SF
exterior ring
of Polygon.
Spatial.numInteriorRing(Polygon)
Returns the OGC SF
number of
interior rings.
Spatial.interiorRingN(Polygon,
Returns the OGC SF
Integer) nth interior
ring.
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.numGeometries(GeomCollection)
Returns the OGC SF
number of
geometries in
the collection.
Spatial.geometryN(GeomCollection,
Returns OGC SF
Integer) the nth
geometry in
the collection.
Oracle
Method Description Specification Result [1] PostGIS MySQL Spatial
Spatial.equals(Geometry,
TRUE if OGC SF
Geometry) the two
geometries [2]
are spatially
equal.
Spatial.disjoint(Geometry,
TRUE if OGC SF
Geometry) the two
geometries [2]
are spatially
disjoint.
Spatial.touches(Geometry,
TRUE if the OGC SF
Geometry) first Geometry
spatially [2]
touches
the other
Geometry.
Spatial.within(Geometry,
TRUE if first OGC SF
Geometry) Geometry is
completely [2]
contained
in second
Geometry.
Spatial.overlaps(Geometry,
TRUE if first OGC SF
Geometry) Geometries
is spatially [2]
overlapping
the other
Geometry.
Spatial.crosses(Geometry,
TRUE if first OGC SF
Geometry) Geometry
crosses [3]
the other
Geometry.
Spatial.intersects(Geometry,
TRUE if first OGC SF
Geometry) Geometry
spatially [2]
intersects
the other
Geometry.
Spatial.contains(Geometry,
TRUE if OGC SF
Geometry) second
Geometry is [2]
completely
contained
in first
Geometry.
Spatial.relate(Geometry,
TRUE if OGC SF
Geometry, the spatial
String) relationship [3]
specified
by the
patternMatrix
holds.
Oracle
Method Description Specification Result PostGIS MySQL Spatial
Spatial.distance(Geometry,
Returns the OGC SF
Geometry) distance
between [1]
the two
geometries.
Oracle
Method Description Specification Result PostGIS MySQL [1] Spatial
Spatial.intersection(Geometry,
Returns a OGC SF
Geometry) Geometry
that is the set
intersection
of the two
geometries.
Spatial.difference(Geometry,
Returns a OGC SF
Geometry) Geometry
that is the
closure of the
set difference
of the two
geometries.
Spatial,union(Geometry,
Returns a OGC SF
Geometry) Geometry
that is the
set union
of the two
geometries.
Spatial.symDifference(Geometry,
Returns a OGC SF
Geometry) Geometry
that is the
closure of the
set symmetric
difference
of the two
geometries.
Spatial.buffer(Geometry,
Returns as OGC SF
Double) Geometry
defined by
buffering
a distance
around the
Geometry.
Spatial.convexHull(Geometry)
Returns a OGC SF
Geometry
that is the
convex
hull of the
Geometry.
[1] These functions are currently not implemented in MySQL.They may appear in future releases.
Test whether the bounding box of one geometry intersects the bounding box of another
MySQL specific Functions for Testing Spatial Relationships between Minimal Bounding Boxes
These functions are only supported on MySQL.
Method Result
MySQL.mbrEqual(Geometry, Geometry)
MySQL.mbrDisjoint(Geometry, Geometry)
MySQL.mbrIntersects(Geometry, Geometry)
MySQL.mbrTouches(Geometry, Geometry)
MySQL.mbrWithin(Geometry, Geometry)
MySQL.mbrContains(Geometry, Geometry)
MySQL.mbrOverlaps(Geometry, Geometry)
Method Desription
Oracle.sdo_geometry( Creates a SDO_GEOMETRY geometry from the
Integer gtype, passed geometry type, srid, point, element infos and
Integer srid, ordinates.
SDO_POINT point,
SDO_ELEM_INFO_ARRAY elem_info,
SDO_ORDINATE_ARRAY ordinates)
Oracle.sdo_point_type( Creates a SDO_POINT geometry from the passed
Double x, Double y, Double z) ordinates.
Oracle.sdo_elem_info_array( Creates a SDO_ELEM_INFO_ARRAY from the
String numbers) passed comma-separeted integers.
Oracle.sdo_ordinate_array( Creates a SDO_ORDINATE_ARRAY from the passed
String ordinates) comma-separeted doubles.
Examples
The following sections provide some examples of what can be done using spatial methods in JDOQL
queries. In the examples we use a class from the test suite. Here's the source code for reference:
package org.datanucleus.samples.pggeometry;
import org.postgis.LineString;
<jdo>
<package name="org.datanucleus.samples.pggeometry">
<extension vendor-name="datanucleus" key="spatial-dimension" value="2"/>
<extension vendor-name="datanucleus" key="spatial-srid" value="4326"/>
22 Statement Batching
.......................................................................................................................................
When changes are required to be made to an underlying RDBMS datastore, statements are sent
via JDBC. A statement is, in general, a single SQL command, and is then executed. In some
circumstances the statements due to be sent to the datastore are the same JDBC statement several
times. In this case the statement can be batched. This means that a statement is created for the
SQL, and it is passed to the datastore with multiple sets of values before being executed. When it is
executed the SQL is executed for each of the sets of values. DataNucleus allows statement batching
under certain circumstances.
The maximum number of statements that can be included in a batch can be set via a persistence
property datanucleus.rdbms.statementBatchLimit. This defaults to 50. If you set it to -1 then there
is no maximum limit imposed. Setting it to 0 means that batching is turned off.
It should be noted that while batching sounds essential, it is only of any possible use when the
exact same SQL is required to be executed more than 1 times in a row. If a different SQL needs
executing between 2 such statements then no batching is possible anyway.. Let's take an example
In this example the first two statements can be batched together since they are identical and nothing
else separates them. All subsequent statements cannot be batched since no two identical statements
follow each other.
The statements that DataNucleus currently allows for batching are
• Insert of objects. This is not enabled when objects being inserted are using identity value
generation strategy
• Delete of objects
• Insert of container elements/keys/values
• Delete of container elements/keys/values
Please note that if using MySQL, you should also specify the connection URL with the argument
rewriteBatchedStatements=true since MySQL won't actually batch without this
23 Views
.......................................................................................................................................
DataNucleus supports persisting objects to RDBMS datastores, persisting to Tables. The majority
of RDBMS also provide support for Views, providing the equivalent of a read-only SELECT across
various tables. DataNucleus also provides support for querying such Views. This provides more
flexibility to the user where they have data and need to display it in their application. Support for
Views is described below.
When you want to access data according to a View, you are required to provide a class that
will accept the values from the View when queried, and Meta-Data for the class that defines
the View and how it maps onto the provided class. Let's take an example. We have a View
SALEABLE_PRODUCT in our database as follows, defined based on data in a PRODUCT table.
package org.datanucleus.samples.views;
public class SaleableProduct
{
String id;
String name;
double price;
String currency;
<?xml version="1.0"?>
<!DOCTYPE jdo SYSTEM "file:/javax/jdo/jdo.dtd">
<jdo>
<package name="org.datanucleus.samples.views">
<class name="SaleableProduct" identity-type="nondurable" table="SALEABLE_PRODUCT">
<field name="id"/>
<field name="name"/>
<field name="price"/>
<field name="currency"/>
• We've defined our class as using "nondurable" identity. This is an important step since rows
of the View typically don't operate in the same way as rows of a Table, not mapping onto a
persisted updateable object as such
• We've specified the "table", which in this case is the view name - otherwise DataNucleus would
create a name for the view based on the class name.
• We've defined a DataNucleus extension view-definition that defines the view for this class.
If the view doesn't already exist it doesn't matter since DataNucleus (when used with
autoCreateSchema) will execute this construction definition.
• The view-definition can contain macros utilising the names of the fields in the class, and hence
borrowing their column names (if we had defined column names for the fields of the class).
• You can also utilise other classes in the macros, and include them via a DataNucleus MetaData
extension view-imports (not shown here)
• If your View already exists you are still required to provide a view-definition even though
DataNucleus will not be utilising it, since it also uses this attribute as the flag for whether it is a
View or a Table - just make sure that you specify the "table" also in the MetaData.
We can now utilise this class within normal DataNucleus querying operation.
Extent e = pm.getExtent(SaleableProduct.class);
Iterator iter = e.iterator();
while (iter.hasNext())
{
SaleableProduct product = (SaleableProduct)iter.next();
}
Hopefully that has given enough detail on how to create and access views from with a DataNucleus-
enabled application.
24 Datastore API
.......................................................................................................................................
JDO/JPA are APIs for persisting and retrieving objects to/from datastores. They don't provide a way
of accessing the schema of the datastore itself (if it has one). In the case of RDBMS it is useful to
be able to find out what columns there are in a table, or what data types are supported for example.
DataNucleus Access Platform provides an API for this.
The first thing to do is get your hands on the DataNucleus StoreManager and from that the
StoreSchemaHandler. You do this as follows
import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
import org.datanucleus.store.StoreManager;
import org.datanucleus.store.schema.StoreSchemaHandler;
import org.datanucleus.store.rdbms.schema.RDBMSTypesInfo;
we can access the JDBC types information via the "children". They are keyed by the JDBC type
number of the JDBC type (see java.sql.Types). So we can just iterate it
import org.datanucleus.store.rdbms.schema.RDBMSTableInfo;
...
}
import org.datanucleus.store.rdbms.schema.RDBMSTableInfo;
...
}
import org.datanucleus.store.rdbms.schema.RDBMSTableInfo;
...
}
import org.datanucleus.store.rdbms.schema.RDBMSTableInfo;
...
}
25 ODF
.......................................................................................................................................
DataNucleus supports persisting/retrieving objects to/from ODF documents (using the datanucleus-
odf plugin, which makes use of the ODFDOM project). Simply specify your "connectionURL" as
follows
datanucleus.ConnectionURL=odf:file:myfile.ods
replacing "myfile.ods" with your filename, which can be absolute or relative. This connects to a file
on your local machine. You then create your PMF/EMF as normal and use JDO/JPA as normal.
The jars required to use DataNucleus ODF persistence are datanucleus-core, datanucleus-api-jdo/
datanucleus-api-jpa, datanucleus-odf and odftoolkit
There are tutorials available for use of DataNucleus with ODF for JDO and for JPA
Things to bear in mind with ODF usage :-
• Querying can be performed using JDOQL or JPQL. Any filtering/ordering will be performed in-
memory
• Relations : A spreadsheet cannot store related objects directly, since each object
is a row of a particular worksheet. DataNucleus gets around this by storing
the String-form of the identity of the related object in the relation cell. See
26 Excel (XLS)
.......................................................................................................................................
DataNucleus supports persisting/retrieving objects to/from Excel documents (using the datanucleus-
excel plugin, which makes use of the Apache POI project). Simply specify your "connectionURL" as
follows
datanucleus.ConnectionURL=excel:file:myfile.xls
replacing "myfile.xls" with your filename, which can be absolute or relative. This connects to a file on
your local machine. You then create your PMF/EMF as normal and use JDO/JPA as normal.
The jars required to use DataNucleus Excel persistence are datanucleus-core, datanucleus-api-jdo/
datanucleus-api-jpa, datanucleus-excel and apache-poi
There are tutorials available for use of DataNucleus with Excel for JDO and for JPA
Things to bear in mind with Excel usage :-
• Querying can be performed using JDOQL or JPQL. Any filtering/ordering will be performed in-
memory
• Relations : A spreadsheet cannot store related objects directly, since each object is a row of a
particular worksheet. DataNucleus gets around this by storing the String-form of the identity of
the related object in the relation cell.
26.1.1 References
Some references that may be of some use
• A JDO With DataNucleus AccessPlatform using Excel and Eclipse Tutorial That Actually
Works
27 Excel (OOXML)
.......................................................................................................................................
replacing "myfile.xlsx" with your filename, which can be absolute or relative. This connects to a file
on your local machine. You then create your PMF/EMF as normal and use JDO/JPA as normal.
The jars required to use DataNucleus OOXML persistence are datanucleus-core, datanucleus-api-jdo/
datanucleus-api-jpa, datanucleus-excel and apache-poi
There are tutorials available for use of DataNucleus with Excel for JDO and for JPA
Things to bear in mind with OOXML usage :-
• Querying can be performed using JDOQL or JPQL. Any filtering/ordering will be performed in-
memory
• Relations : A spreadsheet cannot store related objects directly, since each object is a row of a
particular worksheet. DataNucleus gets around this by storing the String-form of the identity of
the related object in the relation cell.
28 XML
.......................................................................................................................................
DataNucleus supports persisting/retrieving objects to/from XML documents (using the datanucleus-
xml plugin). Simply specify your "connectionURL" as follows
datanucleus.ConnectionURL=xml:file:myfile.xml
<jdo>
<package name="org.datanucleus.samples.models.company">
<class name="Person" detachable="true" schema="/myproduct/people" table="person">
<field name="personNum">
<extension vendor-name="datanucleus" key="XmlAttribute" value="true"/>
</field>
<field name="firstName" primary-key="true"/> <!-- PK since JAXB requires String -->
<field name="lastName"/>
<field name="bestFriend"/>
</class>
</package>
</jdo>
Things to note :
• schema on class is used to define the "XPath" to the root of the class in XML. You can also use
the extension "xpath" to specify the same thing.
• table on class is used to define the name of the element for an object of the particular class.
• column on field is used to define the name of the element for a field of the particular class.
• XmlAttribute : when set to true denotes that this will appear in the XML file as an attribute of
the overall element for the object
• When a field is primary-key it will gain a JAXB "XmlID" attribute.
• When a field is a relation to another object (and the field is not embedded) then it will gain a
JAXB "XmlIDREF" attribute as a link to the other object.
• Important : JAXB has a limitation for primary keys : there can only be a single PK field, and
it must be a String!
What is generated with the above is as follows
@PersistenceCapable(schema="/myproduct/people", table="person")
public class Person
{
@XmlAttribute
private long personNum;
@PrimaryKey
private String firstName;
@XmlElementWrapper(name="phone-numbers")
@XmlElement(name="phone-number")
@Element(types=String.class)
private Map phoneNumbers = new HashMap();
...
Here's the same example using JPA Annotations (with DataNucleus @Extension/@Extensions
annotations)
29 HBase
.......................................................................................................................................
DataNucleus supports persisting/retrieving objects to/from HBase datastores (using the datanucleus-
hbase plugin, which makes use of the HBase/Hadoop jars). Simply specify your "connectionURL" as
follows
datanucleus.ConnectionURL=hbase[:{server}:{port}]
datanucleus.ConnectionUserName=
datanucleus.ConnectionPassword=
If you just specify the URL as hbase then you have a local HBase datastore, otherwise it tries to
connect to the datastore at {server}:{port}. Alternatively just put "hbase" as the URL and set the
zookeeper details in "hbase-site.xml" as normal. You then create your PMF/EMF as normal and use
JDO/JPA as normal.
The jars required to use DataNucleus HBase persistence are datanucleus-core, datanucleus-api-jdo/
datanucleus-api-jpa, datanucleus-hbase and hbase, hadoop-core, zookeeper.
There are tutorials available for use of DataNucleus with HBase for JDO and for JPA
Things to bear in mind with HBase usage :-
@Column(name="{familyName}:{qualifierName}")
String myField;
replacing {familyName} with the family name you want to use, and {qualifierName} with the column
name (qualifier name in HBase terminology) you want to use. Alternatively if you don't want to
override the default family name (the table name), then you just omit the "{familyName}:" part and
simply specify the column name.
• bloomFilter : An advanced feature available in HBase is Bloom filters, allowing you to improve
lookup times given you have a specific access pattern. Default is NONE. Possible values are:
ROW -> use the row key for the filter, ROWKEY -> use the row key and column key (family
+qualifier) for the filter.
• inMemory : The in-memory flag defaults to false. Setting it to true is not a guarantee that all
blocks of a family are loaded into memory nor that they stay there. It is an elevated priority, to
keep them in memory as soon as they are loaded during a normal retrieval operation, and until
the pressure on the heap (the memory available to the Java-based server processes)is too high, at
which time they need to be discarded by force.
• maxVersions : Per family, you can specify how many versions of each value you want to
keep.The default value is 3, but you may reduce it to 1, for example, in case you know for sure
that you will never want to look at older values.
• keepDeletedCells : ColumnFamilies can optionally keep deleted cells. That means deleted cells
can still be retrieved with Get or Scan operations, as long these operations have a time range
specified that ends before the timestamp of any delete that would affect the cells. This allows for
point in time queries even in the presence of deletes. Deleted cells are still subject to TTL and
there will never be more than "maximum number of versions" deleted cells. A new "raw" scan
options returns all deleted rows and the delete markers.
• compression : HBase has pluggable compression algorithm, default value is NONE. Possible
values GZ, LZO, SNAPPY.
• blockCacheEnabled : As HBase reads entire blocks of data for efficient I/O usage, it retains
these blocks in an in-memory cache so that subsequent reads do not need any disk operation.
The default of true enables the block cache for every read operation. But if your use case only
ever has sequential reads on a particular column family, it is advisable that you disable it from
polluting the block cache by setting it to false.
• timeToLive : HBase supports predicate deletions on the number of versions kept for each value,
but also on specific times. The time-to-live (or TTL) sets a threshold based on the timestamp of a
value and the internal housekeeping is checking automatically if a value exceeds its TTL. If that
is the case, it is dropped during major compactions
To express these options, a format similar to a properties file is used such as:
where:
@PersistenceCapable
@Extensions({
@Extension(vendorName = "datanucleus", key = "hbase.columnFamily.meta.bloomFilter", value = "ROWKEY")
@Extension(vendorName = "datanucleus", key = "hbase.columnFamily.meta.inMemory", value = "true")
})
public class MyClass
{
@PrimaryKey
private long id;
29.1.3 References
Below are some references using this support
30 MongoDB
.......................................................................................................................................
If you just specify the URL as mongodb then you have a local MongoDB datastore called
"DataNucleus", otherwise it tries to connect to the datastore {dbName} at {server}. The multiple
{server} option allows you to run against MongoDB replica sets. You then create your PMF/EMF as
normal and use JDO/JPA as normal.
The jars required to use DataNucleus MongoDB persistence are datanucleus-core, datanucleus-api-
jdo/ datanucleus-api-jpa, datanucleus-mongodb and mongo-java-driver
There are tutorials available for use of DataNucleus with MongoDB for JDO and for JPA
Things to bear in mind with MongoDB usage :-
• Creation of a PMF/EMF will create a MongoClient. This will be closed then the PMF/EMF is
closed.
• Creation of a PM/EM and performing an operation will obtain a DB object from the
MongoClient. This is pooled by the MongoClient so is managed by MongoDB. Closing the PM/
EM will stop using that DB
• You can set the number of connections per host with the persistence property
datanucleus.mongodb.connectionsPerHost
• Querying can be performed using JDOQL or JPQL. Some components of a filter are handled
in the datastore, and the remainder in-memory. Currently any expression of a field (in the same
table), or a literal are handled in-datastore, as are the operators &&, ||, >, >=, <, <=, ==, and !=.
• If you want a query to be runnable on a slave MongoDB instance then you should set the query
extension (JDO) / hint (JPA) slave-ok as true, and when executed it can be run on a slave
instance.
• All objects of a class are persisted to a particular "document" (specifiable with the "table" in
metadata), and a field of a class is persisted to a particular "field" ("column" in the metadata).
• Relations : DataNucleus stores the id of the related object(s) in a field of the owning object.
When a relation is bidirectional both ends of the relation will store the relation information.
• Capped collections : you can specify the extension metadata key mongodb.capped.size as the
number of bytes of the size of the collection for the class in question.
• If you want to specify the max number of connections per host with MongoDB then set the
persistence property datanucleus.mongodb.connectionsPerHost
• The default is to store the object in the field as a sub-document (nested) of the owning document.
Similarly if that sub-object has a field of a persistable type then that can be further nested.
• The alternative is to store each field of the sub-object as a field of the owning document (flat
embedding). Similarly if that sub-object has a field of a persistable type then it can be flat
embedded in the same way
@PersistenceCapable
public class A
{
@Embedded
B b;
...
}
This example uses the default embedding, using a nested document within the owner document, and
could look something like this
@PersistenceCapable
public class A
{
@Embedded
@Extension(vendorName="datanucleus", key="nested", value="false")
B b;
...
}
and this will use flat embedding, looking something like this
@PersistenceCapable
public class A
{
@Element(embedded="true")
Collection<b> bs;
...
}
30.1.3 References
Below are some references using this support
• Sasa Jovancic - Use JPA with MongoDb and Datanucleus
31 Cassandra
.......................................................................................................................................
where it will create a Cassandra cluster with contact points of host1 ( host2, host3 etc), and if the port
is specified on the first host then will use that as the port (no port specified on alternate hosts).
For example, to connect to a local server
datanucleus.ConnectionURL=cassandra:
It is intended to have this plugin feature complete by DataNucleus v4.0, but is available in GitHub for
testing/improvement. The jars required to use DataNucleus Cassandra persistence are datanucleus-
core, datanucleus-api-jdo/ datanucleus-api-jpa, datanucleus-cassandra and cassandra-driver-core
Things to bear in mind with Cassandra usage :-
• Creation of a PMF/EMF will create a Cluster. This will be closed then the PMF/EMF is closed.
• Any PM/EM will use a single Session, by default, shared amongst all PM/EMs.
• If you specify the persistence property datanucleus.cassandra.sessionPerManager to true then
each PM/EM will have its own Session object.
• You can set the number of connections per host with the persistence property
datanucleus.mongodb.connectionsPerHost
• Cassandra doesn't use transactions, so any JDO/JPA transaction operation is a no-op (i.e will be
ignored).
• This uses Cassandra 2.x (and CQL v3.x), not Thrift (like the previous unofficial attempts at a
datanucleus-cassandra plugin used)
• You need to specify the "schema" ( datanucleus.mapping.Schema)
• Queries are evaluated in-datastore when they only have (indexed) members and literals and using
the operators ==, !=, >, >=, <, <=, &&, ||.
• You can query the datastore using JDOQL, JPQL, or CQL
Note that if you choose to use Cassandra CQL Queries then these are not portable to any other datastore. Use
JDOQL/JPQL for portability
Cassandra provides the CQL query language. To take a simple example using the JDO API
You can also query results as List<Object[]> without specifying a specific result type as shown
below.
So we are utilising the JDO API to generate a query and passing in the Cassandra "CQL".
If you wanted to use CQL with the JPA API, you would do
Note that the last argument to createNativeQuery is optional and you would get List<Object[]>
returned otherwise.
32 Neo4j
.......................................................................................................................................
For example
datanucleus.ConnectionURL=neo4j:myNeo4jDB
You then create your PMF/EMF as normal and use JDO/JPA as normal.
The jars required to use DataNucleus Neo4j persistence are datanucleus-core, datanucleus-api-jdo/
datanucleus-api-jpa, datanucleus-neo4j and neo4j
Note that this is for embedded Neo4j. This is because at the time of writing there is no binary
protocol for connecting Java clients to the server with Neo4j. When that is available we would
hope to support it.
There are tutorials available for use of DataNucleus with Neo4j for JDO and for JPA
Things to bear in mind with Neo4j usage :-
• Creation of a PMF/EMF will create a GraphDatabaseService and this is shared by all PM/EM
instances. Since this is for an embedded graph datastore then this is the only logical way to
provide this. Should this plugin be updated to connect to a Neo4J server then this will change.
• Querying can be performed using JDOQL or JPQL. Some components of a filter are handled
in the datastore, and the remainder in-memory. Currently any expression of a field (in the same
'table'), or a literal are handled in-datastore, as are the operators &&, ||, >, >=, <, <=, ==, and !=.
Also the majority of ordering and result clauses are evaluatable in the datastore, as well as query
result range restrictions.
• When an object is persisted it becomes a Node in Neo4j. You define the names of the properties
of that node by specifying the "column" name using JDO/JPA metadata
• Any 1-1, 1-N, M-N, N-1 relation is persisted as a Relationship object in Neo4j and any
positioning of elements in a List or array is preserved via a property on the Relationship.
• If you wanted to specify some neo4j.properties file for use of your embedded database then
specify the persistence property datanucleus.ConnectionPropertiesFile set to the filename.
• This plugin is in prototype stage so would welcome feedback and, better still, some contributions
to fully exploit the power of Neo4j. Register your interest on the DataNucleus Forum
Inventory inventory;
...
}
Set<Product> products;
...
}
String name;
double value;
...
}
When we persist a Store object, which has an Inventory, which has three Product objects, then we get
the following
• Node for the Store, with the "id" is represented as the node id
• Node for the Inventory, with the "id" is represented as the node id
• Relationship between the Store Node and the Inventory Node, with the relationship type as
"SINGLE_VALUED", and with the property DN_FIELD_NAME as "inventory"
• Node for Product #1, with properties for "name" and "value" as well as the "id" represented as
the node id
• Node for Product #2, with properties for "name" and "value" as well as the "id" represented as
the node id
• Node for Product #3, with properties for "name" and "value" as well as the "id" represented as
the node id
• Relationship between the Inventory Node and the Product #1 Node, with the relationship type
"MULTI_VALUED" and the property DN_FIELD_NAME as "products"
• Relationship between the Inventory Node and the Product #2 Node, with the relationship type
"MULTI_VALUED" and the property DN_FIELD_NAME as "products"
• Relationship between the Inventory Node and the Product #3 Node, with the relationship type
"MULTI_VALUED" and the property DN_FIELD_NAME as "products"
33 JSON
.......................................................................................................................................
DataNucleus supports persisting/retrieving objects to/from JSON documents (using the datanucleus-
json plugin). Simply specify your "connectionURL" as follows
datanucleus.ConnectionURL=json:{url}
replacing "{url}" with some URL of your choice (e.g "http://www.mydomain.com/somepath/"). You
then create your PMF/EMF as normal and use JDO/JPA as normal.
Things to bear in mind with JSON usage :-
• Querying can be performed using JDOQL or JPQL. Any filtering/ordering will be performed in-
memory
• Relations : DataNucleus stores the id of the related object(s) in the element of the field. If a
relation is bidirectional then it will be stored at both ends of the relation; this facilitates easy
access to the related object with no need to do a query to find it.
Extension Element
Metadata API Attachment Extension Description
JDO /jdo/package/class/ url Defines the location of the
extension resources/objects for the
class
<jdo>
<package name="org.datanucleus.samples.models.company">
<class name="Person" detachable="true">
<extension vendor-name="datanucleus" key="url" value="/Person"/>
</class>
</package>
</jdo>
In this example, the url extension identifies the Person resources/objects as /Person. The persistence
operations will be relative to this path. e.g /Person/{primary key} will be used for PUT (update), GET
(fetch) and DELETE (delete) methods.
34 Amazon S3
.......................................................................................................................................
DataNucleus supports persisting/retrieving objects to/from Amazon Simple Storage Service (using the
datanucleus-json plugin). Simply specify your connection details as follows
datanucleus.ConnectionURL=amazons3:http://s3.amazonaws.com/
datanucleus.ConnectionUserName={Access Key ID}
datanucleus.ConnectionPassword={Secret Access Key}
datanucleus.cloud.storage.bucket={bucket}
You then create your PMF/EMF as normal and use JDO/JPA as normal.
Things to bear in mind with Amazon S3 usage :-
• Querying can be performed using JDOQL or JPQL. Any filtering/ordering will be performed in-
memory
34.1.1 References
Below are some references using this support
35 GoogleStorage
.......................................................................................................................................
datanucleus.ConnectionURL=googlestorage:http://commondatastorage.googleapis.com/
datanucleus.ConnectionUserName={Access Key ID}
datanucleus.ConnectionPassword={Secret Access Key}
datanucleus.cloud.storage.bucket={bucket}
You then create your PMF/EMF as normal and use JDO/JPA as normal.
Things to bear in mind with GoogleStorage usage :-
• Querying can be performed using JDOQL or JPQL. Any filtering/ordering will be performed in-
memory
36 LDAP
.......................................................................................................................................
DataNucleus supports persisting/retrieving objects to/from LDAP datastores (using the datanucleus-
ldap plugin). If you wish to help out development of this plugin either by contributing or by
sponsoring particular functionality please contact us via the DataNucleus Forum.
datanucleus.ConnectionDriverName=com.sun.jndi.ldap.LdapCtxFactory
datanucleus.ConnectionURL=ldap://localhost:10389
datanucleus.ConnectionUserName=uid=admin,ou=system
datanucleus.ConnectionPassword=secret
36.1.2 Queries
Access Platform allows you to query the objects in the datastore using the following
• JDOQL - language based around the objects that are persisted and using Java-type syntax
• JPQL - language based around the objects that are persisted and using SQL-like syntax
Queries are evaluated in-memory.
• DN matching
• Attribute matching
• LDAP hierarchies (deprecated)
It is also possible to store persistable objects embedded. Note that there is inbuilt logic for deciding
which of these mapping strategies to use for a relationship. You can explicitly set this with the
metadata extension for the field/property mapping-strategy and it can be set to dn or attribute.
36.1.5 Examples
Here's an example using JDO XML MetaData:
<jdo>
<package name="org.datanucleus.samples.models.company">
<class name="Group" table="ou=Groups,dc=example,dc=com" schema="top,groupOfNames" detachable=
<field name="name" column="cn" primary-key="true" />
<field name="users" column="member" />
</class>
For the class as a whole we use the table attribute to set the distinguished name of the container
under which to store objects of a type. So, for example, we are mapping all objects of class Group as
subordinates to "ou=Groups,dc=example,dc=com". You can also use the extension "dn" to specify the
same thing.
For the class as a whole we use the schema attribute to define the object classes of the
LDAP entry. So, for example, all objects of type Person are mapped to the common
"top,person,organizationalPerson,inetOrgPerson" object classes in LDAP. You can also use the
extension "objectClass" to specify the same thing.
For each field we use the column attribute to define the LDAP attribute that we are mapping this field
to. So, for example, we map the Group "name" to "cn" in our LDAP. You can also use the extension
"attribute" to specify the same thing.
Some resulting LDAP entries would look like this:
dn: cn=Sales,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: Sales
member: cn=1,ou=Users,dc=example,dc=com
dn: cn=1,ou=Users,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: 1
givenName: Bugs
sn: Bunny
@PersistenceCapable(table="ou=Groups,dc=example,dc=com", schema="top,groupOfNames")
public class Group
{
@PrimaryKey
@Column(name = "cn")
String name;
@Column(name = "member")
protected Set<Person> users = new HashSet<Person>();
}
@PersistenceCapable(table="ou=Users,dc=example,dc=com", schema="top,person,organizationalPerson,inetOrgPe
public class Person
{
@PrimaryKey
@Column(name = "cn")
private long personNum;
@Column(name = "givenName")
private String firstName;
@Column(name = "sn")
private String lastName;
}
@Entity
@Table(name="ou=Groups,dc=example,dc=com", schema="top,groupOfNames")
public class Group
{
@Id
@Extension(key="attribute", value="cn")
String name;
@OneToMany
@Extension(key="attribute", value="member")
protected Set users = new HashSet();
}
@Entity
@Table(name="ou=Groups,dc=example,dc=com", schema="top,person,organizationalPerson,inetOrgPerson")
public class Person
{
@Id
@Extension(key="attribute", value="roomNumber")
private long personNum;
@Extension(key="attribute", value="cn")
private String firstName;
@Extension(key="attribute", value="sn")
private String lastName;
}
37 Relations by DN
.......................................................................................................................................
• Unidirectional
• Bidirectional
We have a flat LDAP tree with one container for all the departments and one container for all the
employees. We have two Java classes, Department and Employee. The Department class contains a
Collection of type Employee. The Employee knows nothing about the Department it belongs to.
There are 2 ways that we can persist this relationship in LDAP because the DN reference could be
stored at the one or at the other LDAP entry.
dn: cn=Sales,ou=Departments,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: Sales
member: cn=Bugs Bunny,ou=Employees,dc=example,dc=com
member: cn=Daffy Duck,ou=Employees,dc=example,dc=com
<jdo>
<package name="com.example">
<class name="Department" table="ou=Departments,dc=example,dc=com" schema="top,groupOfNames">
<field name="name" primary-key="true" column="cn" />
<field name="employees" column="member">
<extension vendor-name="datanucleus" key="empty-value" value="uid=admin,ou=system"/>
</field>
</class>
<class name="Employee" table="ou=Employees,dc=example,dc=com" schema="top,person,organizationalPe
<field name="fullName" primary-key="true column="cn" />
<field name="firstName" column="givenName" />
<field name="lastName" column="sn" />
</class>
</package>
</jdo>
So we define that the attribute member should be used to persist the relationship of field employees.
Note: We use the extension empty-value here. The groupOfNames object class defines the member
attribute as mandatory attribute. In case where you remove all the employees from a department
would delete all member attributes which isn't allowed. In that case DataNucleus adds this empty
value to the member attribute. This value is also filtered when DataNucleus reads the object from
LDAP.
<jdo>
<package name="com.example">
<class name="Department" table="ou=Departments,dc=example,dc=com" schema="top,groupOfNames">
<field name="name" primary-key="true" column="cn" />
<field name="employees">
<element column="departmentNumber" />
</field>
</class>
<class name="Employee" table="ou=Employees,dc=example,dc=com" schema="top,person,organizationalPe
<field name="fullName" primary-key="true column="cn" />
<field name="firstName" column="givenName" />
<field name="lastName" column="sn" />
</class>
</package>
</jdo>
We need to define the relationship at the department metadata because the employee doesn't know
about the department it belongs to. With the <element> tag we specify that the relationship should
be persisted at the other side, the column attribute defines the LDAP attribute to use. In this case the
relationship is persisted in the departmentNumber attribute at the employee entry.
We have a flat LDAP tree with one container for all the departments and one container for all the
employees. We have two Java classes, Department and Employee. The Department class contains a
Collection of type Employee. Now each Employee has a reference to its Department.
It is possible to persist this relationship on both sides.
dn: cn=Sales,ou=Departments,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: Sales
member: cn=Bugs Bunny,ou=Employees,dc=example,dc=com
member: cn=Daffy Duck,ou=Employees,dc=example,dc=com
<jdo>
<package name="com.example">
<class name="Department" table="ou=Departments,dc=example,dc=com" schema="top,groupOfNames">
<field name="name" primary-key="true" column="cn" />
<field name="employees" column="member">
<extension vendor-name="datanucleus" key="empty-value" value="uid=admin,ou=system"/>
</field>
</class>
<class name="Employee" table="ou=Employees,dc=example,dc=com" schema="top,person,organizationalPe
<field name="fullName" primary-key="true column="cn" />
<field name="firstName" column="givenName" />
<field name="lastName" column="sn" />
<field name="department" mapped-by="employees" />
</class>
</package>
</jdo>
In this case we store the relation at the department entry side in a multi-valued attribute member.
Now the employee metadata contains a department field that is mapped-by the employees field of
department.
Note: We use the extension empty-value here. The groupOfNames object class defines the member
attribute as mandatory attribute. In case where you remove all the employees from a department
would delete all member attributes which isn't allowed. In that case DataNucleus adds this empty
value to the member attribute. This value is also filtered when DataNucleus reads the object from
LDAP.
38 Relations by Attribute
.......................................................................................................................................
• Unidirectional
• Bidirectional
We have a flat LDAP tree with one container for all the departments and one container for all the
employees. We have two Java classes, Department and Employee. The Department class contains a
Collection of type Employee. The Employee knows nothing about the Department it belongs to.
There are 2 ways that we can persist this relationship in LDAP because the reference could be stored
at the one or at the other LDAP entry.
dn: ou=Sales,ou=Departments,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
objectClass: extensibleObject
ou: Sales
memberUid: bbunny
memberUid: dduck
<jdo>
<package name="com.example">
<class name="Department" table="ou=Departments,dc=example,dc=com" schema="top,organizationalUnit,
<field name="name" primary-key="true" column="ou" />
<field name="employees" column="memberUid">
<join column="uid" />
</field>
</class>
<class name="Employee" table="ou=Employees,dc=example,dc=com" schema="top,person,organizationalPe
So we define that the attribute memberUid at the department entry should be used to persist the
relationship of field employees
The important thing here is the <join> tag and its column. Firstly it signals DataNucleus to use
attribute mapping. Secondly it specifies the attribute at the other side that should be used for
relationship mapping. In our case, when we establish a relationship between a Department and an
Employee, the uid value of the employee entry is stored in the memberUid attribute of the department
entry.
dn: uid=bbunny,ou=Employees,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: bbunny
cn: Bugs Bunny
givenName: Bugs
sn: Bunny
departmentNumber: Sales
<jdo>
<package name="com.example">
<class name="Department" table="ou=Departments,dc=example,dc=com" schema="top,organizationalUnit"
<field name="name" primary-key="true" column="ou" />
<field name="employees">
<element column="departmentNumber" />
<join column="ou" />
</field>
</class>
<class name="Employee" table="ou=Employees,dc=example,dc=com" schema="top,person,organizationalPe
<field name="fullName" primary-key="true column="cn" />
<field name="firstName" column="givenName" />
<field name="lastName" column="sn" />
<field name="uid" column="uid" />
</class>
</package>
</jdo>
We need to define the relationship at the department metadata because the employee doesn't know
about the department it belongs to.
With the <element> tag we specify that the relationship should be persisted at the other side and the
column attribute defines the LDAP attribute to use. In this case the relationship is persisted in the
departmentNumber attribute at the employee entry.
The important thing here is the <join> tag and its column. As before it signals DataNucleus to use
attribute mapping. Now, as the relation is persisted at the other side, it specifies the attribute at this
side that should be used for relationship mapping. In our case, when we establish a relationship
between a Department and an Employee, the ou value of the department entry is stored in the
departmentNumber attribute of the employee entry.
We have a flat LDAP tree with one container for all the departments and one container for all the
employees. We have two Java classes, Department and Employee. The Department class contains a
Collection of type Employee. Now each Employee has a reference to its Department.
It is possible to persist this relationship on both sides.
dn: uid=bbunny,ou=Employees,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: bbunny
cn: Bugs Bunny
givenName: Bugs
sn: Bunny
departmentNumber: Sales
<jdo>
<package name="com.example">
<class name="Department" table="ou=Departments,dc=example,dc=com" schema="top,organizationalUnit"
<field name="name" primary-key="true" column="ou" />
<field name="employees" mapped-by="department" />
</class>
<class name="Employee" table="ou=Employees,dc=example,dc=com" schema="top,person,organizationalPe
<field name="fullName" primary-key="true column="cn" />
<field name="firstName" column="givenName" />
<field name="lastName" column="sn" />
<field name="uid" column="uid" />
<field name="department" column="departmentNumber">
<join column="ou" />
</field>
</class>
</package>
</jdo>
In this case we store the relation at the employee entry side in a single-valued attribute
departmentNumber. With the <join> tag and its column we specify that the ou value of the
department entry should be used as join value. Also note that employee field of Department is
mapped-by the department field of the Employee.
39 Relations by Hierarchy
.......................................................................................................................................
In the LDAP tree we have departments (Sales and Engineering) and each department holds some
associated employees. In our Java classes each Employee object knows its Department but not vice-
versa.
The JDO metadata looks like this:
<jdo>
<package name="com.example">
<class name="Department" table="dc=example,dc=com" schema="top,organizationalUnit">
<field name="name" primary-key="true" column="ou" />
</class>
The Department objects are persisted directly under dc=example,dc=com. The Employee class has
a dynamic DN definition {department}. So the DN of the Department instance is used as container for
Employee objects.
Now the Department class has a Collection containing references to its Employees.
The JDO metadata looks like this:
<jdo>
<package name="com.example">
<class name="Department" table="dc=example,dc=com" schema="top,organizationalUnit">
<field name="name" primary-key="true" column="ou" />
<field name="employees" mapped-by="department"/>
</class>
We added a new employees field to the Department class that is mapped-by the department field of the
Employee class.
Please note: When loading the parent object all child object are loaded immediately. For a large
number of child entries this may lead to performance and/or memory problems.
In the LDAP tree we have persons and each person has one account. Each Account object knows to
which Person it belongs to, but not vice-versa.
The JDO metadata looks like this:
<jdo>
<package name="com.example">
<class name="Person" table="ou=People,dc=example,dc=com" schema="top,person,organizationalPerson,
<field name="fullName" primary-key="true column="cn" />
<field name="firstName" column="givenName" />
<field name="lastName" column="sn" />
</class>
The Person objects are persisted directly under ou=People,dc=example,dc=com. The Account class
has a dynamic DN definition {person}. So the DN of the Person instance is used as container for the
Account object.
<jdo>
<package name="com.example">
<class name="Person" table="ou=People,dc=example,dc=com" schema="top,person,organizationalPerson,
<field name="fullName" primary-key="true column="cn" />
<field name="firstName" column="givenName" />
<field name="lastName" column="sn" />
<field name="account" mapped-by="person" />
</class>
We added a new account field to the Person class that is mapped-by the person field of the Account
class.
40 Embedded Objects
.......................................................................................................................................
This entry contains multiple type of information: a person, its address and its account data. So we will
create the following Java classes:
The JDO metadata to map these objects to one LDAP entry would look like this:
<jdo>
<package name="com.example">
<class name="Person" table="ou=Employees,dc=example,dc=com" schema="top,person,organizationalPers
<field name="fullName" primary-key="true" column="cn" />
<field name="firstName" column="givenName" />
<field name="lastName" column="sn" />
<field name="account">
<embedded null-indicator-column="uid">
<field name="id" column="uid" />
<field name="password" column="userPassword" />
</embedded>
</field>
<field name="address">
<embedded null-indicator-column="l">
<field name="zip" column="postalCode" />
<field name="city" column="l" />
<field name="street" column="street" />
</embedded>
</field>
</class>
<class name="Account" embedded-only="true">
<field name="uid" />
<field name="password" />
</class>
<class name="Address" embedded-only="true">
<field name="zip" />
<field name="city" />
<field name="street" />
</class>
</package>
</jdo>
41 NeoDatis
.......................................................................................................................................
datanucleus.ConnectionURL=neodatis:file:neodatisdb.odb
Replacing "neodatis.odb" by your filename for the datastore, and can be absolute OR relative.
The following persistence properties will connect to embedded-server-based NeoDatis running with
a local file
datanucleus.ConnectionURL=neodatis:server:{my_neodatis_file}
datanucleus.ConnectionUserName=
datanucleus.ConnectionPassword=
datanucleus.ConnectionURL=neodatis:{neodatis_host}:{neodatis_port}/{identifier}
datanucleus.ConnectionUserName=
datanucleus.ConnectionPassword=
Neodatis doesn't itself use such URLs so it was necessary to define this DataNucleus-specific way of
addressing Neodatis.
So you create your PersistenceManagerFactory or EntityManagerFactory with these properties.
Thereafter you have the full power of the JDO or JPA APIs at your disposal, for your NeoDatis
datastore.
41.1.2 Queries
AccessPlatform allows you to query the objects in the datastore using the following
• JDOQL - language based around the objects that are persisted and using Java-type syntax
• JPQL - language based around the objects that are persisted and using SQL-like syntax
Note that if you choose to use NeoDatis Native Queries then these are not portable to any other datastore. Use
JDOQL/JPQL for portability
NeoDatis provides its own "native" query interface, and if you are using the JDO API you can utilise
this for querying. To take a simple example
So we are utilising the JDO API to generate a query and passing in the NeoDatis "NativeQuery".
Note that if you choose to use NeoDatis Criteria Queries then these are not portable to any other datastore.
Use JDOQL/JPQL for portability
NeoDatis provides its own "criteria" query interface, and if you are using the JDO API you can utilise
this for querying. To take a simple example
So we are utilising the JDO API to generate a query and passing in the NeoDatis "CriteriaQuery".
• NeoDatis doesn't have the concept of an "unloaded" field and so when you request an object
from the datastore it comes with its graph of objects. Consequently there is no "lazy loading" and
the consequent impact that can have on memory utilisation.
42 JDO API
.......................................................................................................................................
Java Data Objects (JDO) defines an interface (or API) to persist normal Java objects (or POJO's in
some peoples terminology) to a datastore. JDO doesn't define the type of datastore; it is datastore-
agnostic. You would use the same interface to persist your Java object to RDBMS, or OODBMS, or
XML, or whatever form of data storage. The whole point of using such a standard interface is that
users can, in principle, swap between implementations of JDO without changing their code. Make
sure you have datanucleus-api-jdo.jar in your CLASSPATH for this API.
The process of mapping a class can be split into the following areas
• JDO categorises classes into 3 types. so you firstly decide which type your class is, and mark
the class in that category
• JDO allows fields/properties to be defined for persistence, and you can control which of these
are persisted, and how they are persisted.
• Some datastores allow a level of mapping between the object-oriented world and the structure of
the datastore, and for this you can define (a level of) Object-Relational Mapping (ORM)
Note that with DataNucleus, you can map your classes using JDO MetaData ( XML/ Annotations)
OR using JPA MetaData ( XML/ Annotations) and still use the JDO API with these classes.
At runtime with JDO you start with the creation of a PersistenceManagerFactory (PMF) which
provides the connectivity to the datastore. The connection to the datastore is dependent on a set of
persistence properties defining the datastore location, URL etc as well as behaviour of the persistence
process.
With JDO, to persist/retrieve objects you require a PersistenceManager (PM) that provides the
interface to persistence and querying of the datastore. You can perform persistence and querying
within a transaction if required, or just use it non-transactionally.
JDO allows querying of the datastore using a range of query languages. The most utilised is JDOQL
providing an object-oriented form of querying, whereas some datastores also permit SQL.
If in doubt about how things fit together, please make use of the JDO Tutorial
If you just want to get the JDO API javadocs, then you can access those here (Apache JDO)
• Apache JDO
• JDO 3.1 RC1 Specification
• JDO 3.1 Javadocs
• JDO1 PDF book by Robin Roos
• Apache JDO mailing lists
43 Class Mapping
.......................................................................................................................................
<class name="Hotel">
...
</class>
@PersistenceCapable
public class Hotel
{
...
}
See also :-
or with annotations
@PersistenceAware
public class MyClass
{
...
}
See also :-
43.1.2 Read-Only
You can, if you wish, make a class read-only. This is a DataNucleus extension and you set it as
follows
<class name="MyClass">
<extension vendor-name="datanucleus" key="read-only" value="true"/>
or with annotations
@PersistenceCapable
@Extension(vendorName="datanucleus", key="read-only", value="true")
public class MyClass
{
...
}
44 Datastore Identity
.......................................................................................................................................
@PersistenceCapable(identityType=IdentityType.DATASTORE)
public class MyClass
{
...
}
So you are specifying the identity-type as datastore. You don't need to add this because datastore is
the default, so in the absence of any value, it will be assumed to be 'datastore'.
When you have an inheritance hierarchy, you should specify the identity type in the base class for the
inheritance tree. This is then used for all persistent classes in the tree.
By choosing datastore identity you are handing the process of identity generation to the JDO
implementation. This does not mean that you haven't got any control over how it does this. JDO 2
defines many ways of generating these identities and DataNucleus supports all of these and provides
some more of its own besides.
Defining which one to use is a simple matter of adding a MetaData element to your classes definition,
like this
@PersistenceCapable
@DatastoreIdentity(strategy="sequence", sequence="MY_SEQUENCE")
public class MyClass
{
...
}
Some of the datastore identity strategies require additional attributes, but the specification is
straightforward.
See also :-
• Identity Generation Guide - strategies for generating ids
• MetaData reference for <datastore-identity> element
• Annotations reference for @DatastoreIdentity
Object id = JDOHelper.getObjectId(obj);
You should be aware however that the "identity" is in a complicated form, and is not available as a
simple integer value for example. Again, if you want an identity of that form then you should use
application identity
1[OID]mydomain.MyClass
This is made up of :-
1 = identity number of this object
class-name
The definition of this datastore identity is JDO implementation dependent. As a result you
should not use the org.datanucleus.identity.OID class in your application if you want to remain
implementation independent
DataNucleus allows you the luxury of being able to provide your own datastore identity class so you
can have whatever formatting you want for identities.
You can also access the object from the object class name and the toString() form of the datastore
identity (e.g "1[OID]mydomain.MyClass") like this
Object obj = pm.getObjectById(MyClass.class, mykey);
45 Application Identity
.......................................................................................................................................
For JDO we specify the primary-key and objectid-class. The objectid-class is optional, and is the
class defining the identity for this class (again, if you have a single primary-key field then you can
omit it). Alternatively, if we are using annotations
@PersistenceCapable(objectIdClass=MyIdClass.class)
public class MyClass
{
@Persistent(primaryKey="true")
private long myPrimaryKeyField;
}
When you have an inheritance hierarchy, you should specify the identity type in the base instantiable
class for the inheritance tree. This is then used for all persistent classes in the tree. This means that you
can have superclass(es) using application-identity without any identity fields/properties but using subclass-table
inheritance, and then the base instantiable class is the first persistable class which has the identity field(s).
See also :-
Object id = JDOHelper.getObjectId(obj);
If you are using SingleField identity then you can access it from the object class name and the key
value like this
Object obj = pm.getObjectById(MyClass.class, mykey);
If you are using your own PK class then the mykey value is the toString() form of the identity of your
PK class.
When you choose application identity you are defining which fields of the class are part of the
primary key, and you are taking control of the specification of id's to DataNucleus. Application
identity requires a primary key (PK) class, and each persistent capable class may define a different
class for its primary key, and different persistent capable classes can use the same primary key class,
as appropriate. If you have only a single primary-key field then there are builtin PK classes so you can
forget this section. Where you have more than 1 primary key field, you would define the PK class like
this
or using annotations
@PersistenceCapable(objectIdClass=MyIdClass.class)
public class MyClass
{
...
}
You now need to define the PK class to use. This is simplified for you because if you have only one
PK field then you dont need to define a PK class and you only define it when you have a composite
PK.
An important thing to note is that the PK can only be made up of fields of the following Java types
. mechanism. This provides a PrimaryKey and you don't need to specify the objectid-class. Let's take
an example
or using annotations
@PersistenceCapable
public class MyClass
{
@PrimaryKey
long id;
...
}
So we didnt specify the JDO "objectid-class". You will, of course, have to give the field a value
before persisting the object, either by setting it yourself, or by using a value-strategy on that field.
If you need to create an identity of this form for use in querying via pm.getObjectById() then you can
create the identities in the following way
We have shown an example above for type "long", but you can also use this for the following
Since there are many possible combinations of primary-key fields it is impossible for JDO to provide
a series of builtin composite primary key classes. However the DataNucleus enhancer provides a
mechanism for auto-generating a primary-key class for a persistable class. It follows the rules listed
below and should work for all cases. Obviously if you want to tailor the output of things like the PK
toString() method then you ought to define your own. The enhancer generation of primary-key class is
only enabled if you don't define your own class.
java.lang.String, or java.lang.Long directly. You must follow these rules when defining your primary
key class.
@PersistenceCapable(objectIdClass=ComposedIdKey.class)
public class MyClass
{
@PrimaryKey
String field1;
@PrimaryKey
String field2;
...
}
public ComposedIdKey ()
{
}
/**
* Constructor accepting same input as generated by toString().
*/
public ComposedIdKey(String value)
{
StringTokenizer token = new StringTokenizer (value, "::");
token.nextToken(); // className
this.field1 = token.nextToken(); // field1
this.field2 = token.nextToken(); // field2
}
46 Nondurable Identity
.......................................................................................................................................
@PersistenceCapable(identityType=IdentityType.NONDURABLE)
public class MyClass
{
...
}
DataNucleus provides support for "nondurable" identity for some datastores only currently (RDBMS,
Excel, ODF, MongoDB, HBase). What this means for something like RDBMS is that the table of the
class will not have a primary-key.
47 Compound Identity
.......................................................................................................................................
• 1-1 unidirectional
• 1-N collection bidirectional using ForeignKey
• 1-N map bidirectional using ForeignKey (key stored in value)
In addition we need to define primary key classes for our User and Account classes
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public User.PK user; // Use same name as the real field above
To achieve what we want with the datastore schema we define the MetaData like this
<package name="mydomain">
<class name="User" identity-type="application" objectid-class="User$PK">
<field name="id" primary-key="true"/>
<field name="login" persistence-modifier="persistent">
<column length="20" jdbc-type="VARCHAR"/>
</field>
</class>
Things to note :-
primary-key to ACCOUNT to include the foreign-key. To do this we need to modify the classes
slightly, adding primary-key fields to both classes, and use "application-identity" for both.
In addition we need to define primary key classes for our Account and Address classes
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
©2014, DataNucleus • ALL RIGHTS RESERVED.
{
public long id; // Same name as real field above
public Account.PK account; // Same name as the real field above
47 Compound Identity 189
To achieve what we want with the datastore schema we define the MetaData like this
<package name="mydomain">
<class name="Account" identity-type="application" objectid-class="Account$PK">
<field name="id" primary-key="true"/>
<field name="firstName" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="secondName" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent" mapped-by="account">
<collection element-type="Address"/>
</field>
</class>
Things to note :-
• You must use "application-identity" in both parent and child classes
• In the child Primary Key class, you must have a field with the same name as the relationship in
the child class, and the field in the child Primary Key class must be the same type as the Primary
Key class of the parent
• See also the general instructions for Primary Key classes
• If we had omitted the "id" field from "Address" it would have only been possible to have one
"Address" in the "Account" "addresses" collection due to PK constraints. For that reason we have
the "id" field too.
In addition we need to define primary key classes for our Account and Address classes
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
©2014, DataNucleus • ALL RIGHTS RESERVED.
{
public String alias; // Same name as real field above
public Account.PK account; // Same name as the real field above
47 Compound Identity 192
To achieve what we want with the datastore schema we define the MetaData like this
<package name="com.mydomain">
<class name="Account" objectid-class="Account$PK">
<field name="id" primary-key="true"/>
<field name="firstname" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastname" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent" mapped-by="account">
<map key-type="java.lang.String" value-type="com.mydomain.Address"/>
<key mapped-by="alias"/>
</field>
</class>
Things to note :-
• If we had omitted the "alias" field from "Address" it would have only been possible to have one
"Address" in the "Account" "addresses" collection due to PK constraints. For that reason we have
the "alias" field too as part of the PK.
48 Versioning
.......................................................................................................................................
<package name="mydomain">
<class name="User" table="USER">
<version strategy="version-number" column="VERSION"/>
<field name="name" column="NAME"/>
...
</class>
</package>
@PersistenceCapable
@Version(strategy=VersionStrategy.VERSION_NUMBER, column="VERSION")
public class MyClass
{
...
}
The specification above will create a table with an additional column called "VERSION" that will
store the version of the object.
DataNucleus provides a valuable extension to JDO whereby you can have a field of your class store
the version of the object. This equates to JPA's versioning process whereby you have to have a field
present. To do this lets take a class
and we want to store the version of the object in the field "myVersion". So we specify the metadata as
follows
<package name="mydomain">
<class name="User" table="USER">
<version strategy="version-number">
<extension vendor-name="datanucleus" key="field-name" value="myVersion"/>
</version>
<field name="name" column="NAME"/>
...
<field name="myVersion" column="VERSION"/>
</class>
</package>
@PersistenceCapable
@Version(strategy=VersionStrategy.VERSION_NUMBER, column="VERSION",
extensions={@Extension(vendorName="datanucleus", key="field-name", value="myVersion")})
public class MyClass
{
protected long myVersion;
...
}
and so now objects of our class will have access to the version via the "myVersion" field.
49 Inheritance
.......................................................................................................................................
1. The first and simplest to understand option is where each class has its own table in the datastore.
In JDO this is referred to as new-table.
2. The second way is to select a class to have its fields persisted in the table of its subclass. In JDO
this is referred to as subclass-table
3. The third way is to select a class to have its fields persisted in the table of its superclass. In JDO
this is known as superclass-table.
4. JDO3.1 introduces support for having all classes in an inheritance tree with their own table
containing all fields. This is known as complete-table and is enabled by setting the inheritance
strategy of the root class to use this.
In order to demonstrate the various inheritance strategies we need an example. Here are a few simple
classes representing products in a (online) store. We have an abstract base class, extending this to to
provide something that we can represent any product by. We then provide a few specialisations for
typical products. We will use these classes later when defining how to persistent these objects in the
different inheritance strategies.
JDO imposes a "default" inheritance strategy if none is specified for a class. If the class is a base class
and no inheritance strategy is specified then it will be set to new-table for that class. If the class has a
superclass and no inheritance strategy is specified then it will be set to superclass-table. This means
that, when no strategy is set for the classes in an inheritance tree, they will default to using a single
table managed by the base class.
You can control the "default" strategy chosen by way of a
Please note that at runtime, when you start up your PMF and PM, JDO will only know about the classes that
the PM has been introduced to via method calls. To alleviate this, particularly for subclasses of classes in an
inheritance relationship, you should make use of one of the many available Auto Start Mechanisms.
Please note that you must specify the identity of objects in the root persistable class of the inheritance
hierarchy. You cannot redefine it down the inheritance tree
See also :-
49.1.1 Discriminator
A discriminator is an extra "column" stored alongside data to identify the class of which that
information is part. It is useful when storing objects which have inheritance to provide a quick way of
determining the object type on retrieval. There are two types of discriminator supported by JDO
• class-name : where the actual name of the class is stored as the discriminator
• value-map : where a (typically numeric) value is stored for each class in question, allowing
simple look-up of the class it equates to
You specify a discriminator as follows
<class name="Product">
<inheritance>
<discriminator strategy="class-name"/>
</inheritance>
or with annotations
@PersistenceCapable
@Discriminator(strategy=DiscriminatorStrategy.CLASS_NAME)
public class Product {...}
Applicable to RDBMS
Here we want to have a separate table for each class. This has the advantage of being the most
normalised data definition. It also has the disadvantage of being slower in performance since multiple
tables will need to be accessed to retrieve an object of a sub type. Let's try an example using the
simplest to understand strategy new-table. We have the classes defined above, and we want to persist
our classes each in their own table. We define the Meta-Data for our classes like this
<class name="AbstractProduct">
<inheritance strategy="new-table"/>
<field name="id" primary-key="true">
<column name="PRODUCT_ID"/>
</field>
<field name="name">
<column name="NAME"/>
</field>
<field name="description">
<column name="DESCRIPTION"/>
</field>
</class>
<class name="Product">
<inheritance strategy="new-table"/>
<field name="price">
<column name="PRICE"/>
</field>
</class>
<class name="Book">
<inheritance strategy="new-table"/>
<field name="isbn">
<column name="ISBN"/>
</field>
<field name="author">
<column name="AUTHOR"/>
</field>
<field name="title">
<column name="TITLE"/>
</field>
</class>
<class name="TravelGuide">
<inheritance strategy="new-table"/>
<field name="country">
<column name="COUNTRY"/>
</field>
</class>
<class name="CompactDisc">
<inheritance strategy="new-table"/>
<field name="artist">
<column name="ARTIST"/>
</field>
<field name="title">
<column name="TITLE"/>
</field>
</class>
or with annotations
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
public class AbstractProduct {...}
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
public class Product {...}
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
public class Book {...}
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
public class TravelGuide {...}
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
public class CompactDisc {...}
We use the inheritance element to define the persistence of the inherited classes.
In the datastore, each class in an inheritance tree is represented in its own datastore table (tables
ABSTRACTPRODUCT, PRODUCT, BOOK, TRAVELGUIDE, and COMPACTDISC), with the
subclasses tables' having foreign keys between the primary key and the primary key of the superclass'
table.
In the above example, when we insert a TravelGuide object into the datastore, a row will be inserted
into ABSTRACTPRODUCT, PRODUCT, BOOK, and TRAVELGUIDE.
Applicable to RDBMS
DataNucleus supports persistence of classes in the tables of subclasses where this is required.
This is typically used where you have an abstract base class and it doesn't make sense having a
separate table for that class. In our example we have no real interest in having a separate table for the
AbstractProduct class. So in this case we change one thing in the Meta-Data quoted above. We now
change the definition of AbstractProduct as follows
<class name="AbstractProduct">
<inheritance strategy="subclass-table"/>
<field name="id" primary-key="true">
<column name="PRODUCT_ID"/>
</field>
<field name="name">
<column name="NAME"/>
</field>
<field name="description">
<column name="DESCRIPTION"/>
</field>
</class>
or with annotations
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.SUBCLASS_TABLE)
public class AbstractProduct {...}
This subtle change of use the inheritance element has the effect of using the PRODUCT table for
both the Product and AbstractProduct classes, containing the fields of both classes.
In the above example, when we insert a TravelGuide object into the datastore, a row will be inserted
into PRODUCT, BOOK, and TRAVELGUIDE.
DataNucleus doesn't currently support the use of classes defined with subclass-table strategy as
having relationships where there are more than a single subclass that has a table. If the class has
a single subclass with its own table then there should be no problem.
Applicable to RDBMS
DataNucleus supports persistence of classes in the tables of superclasses where this is required.
This has the advantage that retrieval of an object is a single SQL call to a single table. It also has the
disadvantage that the single table can have a very large number of columns, and database readability
and performance can suffer, and additionally that a discriminator column is required. In our example,
lets ignore the AbstractProduct class for a moment and assume that Product is the base class. We
have no real interest in having separate tables for the Book and CompactDisc classes and want
everything stored in a single table PRODUCT. We change our MetaData as follows
<class name="Product">
<inheritance strategy="new-table">
<discriminator strategy="class-name">
<column name="PRODUCT_TYPE"/>
</discriminator>
</inheritance>
<field name="id" primary-key="true">
<column name="PRODUCT_ID"/>
</field>
<field name="price">
<column name="PRICE"/>
</field>
</class>
<class name="Book">
<inheritance strategy="superclass-table"/>
<field name="isbn">
<column name="ISBN"/>
</field>
<field name="author">
<column name="AUTHOR"/>
</field>
<field name="title">
<column name="TITLE"/>
</field>
</class>
<class name="TravelGuide">
<inheritance strategy="superclass-table"/>
<field name="country">
<column name="COUNTRY"/>
</field>
</class>
<class name="CompactDisc">
<inheritance strategy="superclass-table"/>
<field name="artist">
<column name="ARTIST"/>
</field>
<field name="title">
<column name="DISCTITLE"/>
</field>
</class>
or with annotations
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
public class AbstractProduct {...}
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.SUPERCLASS_TABLE)
public class Product {...}
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.SUPERCLASS_TABLE)
public class Book {...}
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.SUPERCLASS_TABLE)
public class TravelGuide {...}
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.SUPERCLASS_TABLE)
public class CompactDisc {...}
This change of use of the inheritance element has the effect of using the PRODUCT table for all
classes, containing the fields of Product, Book, CompactDisc, and TravelGuide. You will also note
that we used a discriminator element for the Product class. The specification above will result in an
extra column (called PRODUCT_TYPE) being added to the PRODUCT table, and containing the
class name of the object stored. So for a Book it will have "com.mydomain.samples.store.Book" in
that column. This column is used in discriminating which row in the database is of which type. The
final thing to note is that in our classes Book and CompactDisc we have a field that is identically
named. With CompactDisc we have defined that its column will be called DISCTITLE since both of
these fields will be persisted into the same table and would have had identical names otherwise - this
gets around the problem.
In the above example, when we insert a TravelGuide object into the datastore, a row will be inserted
into the PRODUCT table only.
JDO 2 allows two types of discriminators. The example above used a discriminator strategy of class-
name. This inserts the class name into the discriminator column so that we know what the class of
the object really is. The second option is to use a discriminator strategy of value-map. With this we
will define a "value" to be stored in this column for each of our classes. The only thing here is that
we have to define the "value" in the MetaData for ALL classes that use that strategy. So to give the
equivalent example :-
<class name="Product">
<inheritance strategy="new-table">
<discriminator strategy="value-map" value="PRODUCT">
<column name="PRODUCT_TYPE"/>
</discriminator>
</inheritance>
<field name="id" primary-key="true">
<column name="PRODUCT_ID"/>
</field>
<field name="price">
<column name="PRICE"/>
</field>
</class>
<class name="Book">
<inheritance strategy="superclass-table">
<discriminator value="BOOK"/>
</inheritance>
<field name="isbn">
<column name="ISBN"/>
</field>
<field name="author">
<column name="AUTHOR"/>
</field>
<field name="title">
<column name="TITLE"/>
</field>
</class>
<class name="TravelGuide">
<inheritance strategy="superclass-table">
<discriminator value="TRAVELGUIDE"/>
</inheritance>
<field name="country">
<column name="COUNTRY"/>
</field>
</class>
<class name="CompactDisc">
<inheritance strategy="superclass-table">
<discriminator value="COMPACTDISC"/>
</inheritance>
<field name="artist">
<column name="ARTIST"/>
</field>
<field name="title">
<column name="DISCTITLE"/>
</field>
</class>
As you can see from the MetaData DTD it is possible to specify the column details for the
discriminator. DataNucleus supports this, but only currently supports the following values of jdbc-
type : VARCHAR, CHAR, INTEGER, BIGINT, NUMERIC. The default column type will be a
VARCHAR.
Applicable to RDBMS, Neo4j, NeoDatis, Excel, OOXML, ODF, HBase, JSON, AmazonS3, GoogleStorage,
MongoDB, LDAP
With "complete-table" we define the strategy on the root class of the inheritance tree and it applies to
all subclasses. Each class is persisted into its own table, having columns for all fields (of the class in
question plus all fields of superclasses). So taking the same classes as used above
<class name="Product">
<inheritance strategy="complete-table"/>
<field name="id" primary-key="true">
<column name="PRODUCT_ID"/>
</field>
<field name="price">
<column name="PRICE"/>
</field>
</class>
<class name="Book">
<field name="isbn">
<column name="ISBN"/>
</field>
<field name="author">
<column name="AUTHOR"/>
</field>
<field name="title">
<column name="TITLE"/>
</field>
</class>
<class name="TravelGuide">
<field name="country">
<column name="COUNTRY"/>
</field>
</class>
<class name="CompactDisc">
<field name="artist">
<column name="ARTIST"/>
</field>
<field name="title">
<column name="DISCTITLE"/>
</field>
</class>
or with annotations
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.COMPLETE_TABLE)
public class AbstractProduct {...}
So the key thing is the specification of inheritance strategy at the root only. This then implies a
datastore schema as follows
So any object of explicit type Book is persisted into the table "BOOK". Similarly any TravelGuide is
persisted into the table "TRAVELGUIDE". In addition if any class in the inheritance tree is abstract
then it won't have a table since there cannot be any instances of that type. DataNucleus currently has
limitations when using a class using this inheritance as the element of a collection.
JDO provides particular mechanisms for retrieving inheritance trees. These are accessed via the
Extent/Query interface. Taking our example above, we can then do
tx.begin();
Extent e = pm.getExtent(com.mydomain.samples.store.Product.class, true);
Query q = pm.newQuery(e);
Collection c=(Collection)q.execute();
tx.commit();
The second parameter passed to pm.getExtent relates to whether to return subclasses. So if we pass in
the root of the inheritance tree (Product in our case) we get all objects in this inheritance tree returned.
You can, of course, use far more elaborate queries using JDOQL, but this is just to highlight the
method of retrieval of subclasses.
50 Fields/Properties
.......................................................................................................................................
@PersistenceCapable
public class MyClass
{
@Persistent
Date birthday;
@NotPersistent
String someOtherField;
}
So, using annotations, we have marked the field birthday as persistent, whereas field someOtherField
is not persisted. Please note that in this particular case, Date is by default persistent so we could
omit the @Persistent annotation (with non-default-persistent types we would definitely need the
annotation). Using XML MetaData we would have done
<class name="MyClass">
<field name="birthday" persistence-modifier="persistent"/>
<field name="someOtherField" persistence-modifier="none"/>
</class>
Please note that the field Java type defines whether it is, by default, persistable. Look at the
Types Guide and if the type has a tick in the column "Persistent?" then you don't need to mark the
persistence-modifier as "persistent".
Requirement : you have a property in the class with Java Bean getter/setter methods. These
methods can be public, protected, private or package access, but cannot be static. The class
must have BOTH getter AND setter methods.
An example of how to define the persistence of a property is shown below
@PersistenceCapable
public class MyClass
{
@Persistent
Date getBirthday()
{
...
}
So, using annotations, we have marked this class as persistent, and the getter is marked as persistent.
By default a property is non-persistent, so we have no need in specifying the someOtherField as not
persistent. Using XML MetaData we would have done
<class name="MyClass">
<property name="birthday" persistence-modifier="persistent"/>
</class>>
so we have changed the field "someField" specified in the persistent superclass "HotelSuperclass" to
not be part of the DFG.
<jdo>
<package name="mydomain">
<class name="Person" detachable="true" table="People">
<field name="personNum">
<column position="0"/>
</field>
<field name="firstName">
<column position="1"/>
</field>
<field name="lastName">
<column position="2"/>
</field>
</class>
</package>
</jdo>
@PersistenceCapable(table="People")
public class Person
{
@Column(position=0)
long personNum;
@Column(position=1)
String firstName;
@Column(position=2)
String lastName;
}
51 Java Types
.......................................................................................................................................
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 datanucleus-
[4] core
java.lang.Object datanucleus-
core
java.lang.String datanucleus-
core
java.lang.StringBuffer datanucleus-
[3] 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 datanucleus-
[7] core
java.util.HashMap datanucleus-
core
java.util.HashSet datanucleus-
core
java.util.Hashtable datanucleus-
core
java.util.LinkedHashMap datanucleus-
[5] core
java.util.LinkedHashSet datanucleus-
[6] 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 datanucleus-
[2] core
java.util.SortedSet datanucleus-
[1] core
java.util.Stack datanucleus-
core
java.util.TimeZone datanucleus-
core
java.util.TreeMap datanucleus-
[2] core
java.util.TreeSet datanucleus-
[1] 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
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
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.
51.1.3 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 column jdbc-type.
An extension to this is where you have an Enum that defines its own "value"s for the different enum
options.
With the default persistence it would persist as String-based, so persisting "RED" "GREEN" "BLUE"
etc. With jdbc-type as INTEGER it would persist 0, 1, 2, 3 being the ordinal values. If you define the
metadata as
@Extensions({
@Extension(vendorName="datanucleus", key="enum-getter-by-value", value="getEnumByValue"),
@Extension(vendorName="datanucleus", 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.
51.1.4 TypeConverters
By default DataNucleus will store the value using its own internal configuration/default for the java
type and for the datastore. The user can, however, change that by making use of a TypeConverter.
You firstly need to define the TypeConverter class (assuming you aren't going to use an internal
DataNucleus converter, and for this you should refer to the TypeConverter plugin-point. Once you
have the converter defined, and registered in a plugin.xml under a name you then mark the field/
property to use it
In this case we have a String field but we want to serialise it, not using normal Java serialisation but
using the "Kryo" library. When it is stored it will be converted into a serialised form and when read
back in will be deserialised. You can see the example Kryo TypeConverter over on GitHub.
52 Value Generation
.......................................................................................................................................
• native - this is the default and allows DataNucleus to choose the most suitable for the datastore.
• sequence - this uses a datastore sequence (if supported by the datastore)
• identity - these use autoincrement/identity/serial features in the datastore (if supported by the
datastore)
• increment - this is datastore neutral and increments a sequence value using a table.
• uuid-string - this is a UUID in string form
• uuid-hex - this is a UUID in hexadecimal form
• uuid - provides a pure UUID utilising the JDK1.5 UUID class
• user-supplied value generators - allows you to hook in your own identity generator
See also :-
Please note that by defining a value-strategy for a field then it will, by default, always
generate a value for that field on persist. If the field can store nulls and you only want it to
generate the value at persist when it is null (i.e you haven't assigned a value yourself) then
you can add the extension "strategy-when-notnull" as false
52.1.1 native
With this strategy DataNucleus will choose the most appropriate strategy for the datastore being used.
If you define the field as String-based then it will choose uuid-hex. Otherwise the field is numeric in
which case it chooses identity if supported, otherwise sequence if supported, otherwise increment
if supported otherwise throws an exception. On RDBMS you can get the behaviour used up until DN
v3.0 by specifying the persistence property datanucleus.rdbms.useLegacyNativeValueStrategy as
true
52.1.2 sequence
A sequence is a user-defined database function that generates a sequence of unique numeric ids.
The unique identifier value returned from the database is translated to a java type: java.lang.Long.
DataNucleus supports sequences for the following datastores:
• Oracle
• PostgreSQL
• SAP DB
• DB2
• Firebird
• HSQLDB
• H2
• Derby (from v10.6)
• SQLServer (from v2012)
• NuoDB
To configure a class to use either of these generation methods with datastore identity you simply add
this to the class' Meta-Data
or using annotations
@PersistenceCapable
@DatastoreIdentity(strategy="sequence", sequence="yourseq"/>
@Sequence(name="yourseq", datastore-sequence="YOUR_SEQUENCE_NAME", strategy=NONCONTIGUOUS/>
public class MyClass
You replace "YOUR_SEQUENCE_NAME" with your sequence name. To configure a class to use
either of these generation methods using application identity you would add the following to the class'
Meta-Data
or using annotations
@PersistenceCapable
@Sequence(name="yourseq", datastore-sequence="YOUR_SEQUENCE_NAME" strategy=NONCONTIGUOUS/>
public class MyClass
{
@Persistent(valueStrategy="sequence", sequence="yourseq"/>
private long myfield;
...
}
If the sequence does not yet exist in the database at the time DataNucleus needs a new unique
identifier, a new sequence is created in the database based on the JDO Meta-Data configuration.
Additional properties for configuring sequences are set in the JDO Meta-Data, see the available
properties below. Unsupported properties by a database are silently ignored by DataNucleus.
This value generator will generate values unique across different JVMs
52.1.3 identity
Auto-increment/identity/serial are primary key columns that are populated when a row is inserted
in the table. These use the databases own keywords on table creation and so rely on having the table
structure either created by DataNucleus or having the column with the necessary keyword.
DataNucleus supports auto-increment/identity/serial keys for many databases including :
• DB2 (IDENTITY)
• MySQL (AUTOINCREMENT)
• MSSQL (IDENTITY)
• Sybase (IDENTITY)
• HSQLDB (IDENTITY)
• H2 (IDENTITY)
• PostgreSQL (SERIAL)
• Derby (IDENTITY)
• MongoDB - String based
• Neo4j - long based
• NuoDB (IDENTITY)
This generation strategy should only be used if there is a single "root" table for the inheritance
tree. If you have more than 1 root table (e.g using subclass-table inheritance) then you should
choose a different generation strategy
For a class using datastore identity you need to set the strategy attribute. You can configure the Meta-
Data for the class something like this (replacing 'myclass' with your class name) :
<class name="myclass">
<datastore-identity strategy="identity"/>
...
</class>
For a class using application identity you need to set the value-strategy attribute on the primary key
field. You can configure the Meta-Data for the class something like this (replacing 'myclass' and
'myfield' with your class and field names) :
Please be aware that if you have an inheritance tree with the base class defined as using "identity"
then the column definition for the PK of the base table will be defined as "AUTO_INCREMENT"
or "IDENTITY" or "SERIAL" (dependent on the RDBMS) and all subtables will NOT have this
identifier added to their PK column definitions. This is because the identities are assigned in the base
table (since all objects will have an entry in the base table).
Please note that if using optimistic transactions, this strategy will mean that the value is only set
when the object is actually persisted (i.e at flush() or commit())
This value generator will generate values unique across different JVMs
52.1.4 increment
This method is database neutral and uses a sequence table that holds an incrementing sequence value.
The unique identifier value returned from the database is translated to a java type: java.lang.Long.
This strategy will work with any datastore. This method require a sequence table in the database and
creates one if doesn't exist.
To configure a datastore identity class to use this generation method you simply add this to the classes
Meta-Data.
To configure an application identity class to use this generation method you simply add this to the
class' Meta-Data. If your class is in an inheritance tree you should define this for the base class only.
Additional properties for configuring this generator are set in the JDO Meta-Data, see the available
properties below. Unsupported properties are silently ignored by DataNucleus.
This value generator will generate values unique across different JVMs
52.1.5 uuid-string
This generator creates identities with 16 characters in string format. The identity contains the IP
address of the local machine where DataNucleus is running, as well as other necessary components to
provide uniqueness across time.
This generator can be used in concurrent applications. It is especially useful in situations where
large numbers of transactions within a certain amount of time have to be made, and the additional
overhead of synchronizing the concurrent creation of unique identifiers through the database would
break performance limits. It doesn't require datastore access to generate the identities and so has
performance benefits over some of the other generators.
For a class using datastore identity you need to add metadata something like the following
To configure an application identity class to use this generation method you simply add this to the
class' JDO Meta-Data.
52.1.6 uuid-hex
This generator creates identities with 32 characters in hexadecimal format. The identity contains the
IP address of the local machine where DataNucleus is running, as well as other necessary components
to provide uniqueness across time.
This generator can be used in concurrent applications. It is especially useful in situations where
large numbers of transactions within a certain amount of time have to be made, and the additional
overhead of synchronizing the concurrent creation of unique identifiers through the database would
break performance limits. It doesn't require datastore access to generate the identities and so has
performance benefits over some of the other generators.
For a class using datastore identity you need to add metadata something like the following
To configure an application identity class to use this generation method you simply add this to the
class' JDO Meta-Data.
52.1.7 datastore-uuid-hex
This method is like the "uuid-hex" option above except that it utilises datastore capabilities to
generate the UUIDHEX code. Consequently this only works on some RDBMS (MSSQL, MySQL).
The disadvantage of this strategy is that it makes a call to the datastore for each new UUID required.
The generated UUID is in the same form as the AUID strategy where identities are generated in
memory and so the AUID strategy is the recommended choice relative to this option.
For a class using datastore identity you need to add metadata something like the following
To configure an application identity class to use this generation method you simply add this to the
class' JDO Meta-Data.
52.1.8 max
This method is database neutral and uses the "select max(column) from table" + 1 strategy to create
unique ids. The unique identifier value returned from the database is translated to a java type:
java.lang.Long. It is however not recommended by DataNucleus since it makes a DB call for
every record to be inserted and hence is inefficient. Each DB call will run a scan in all table
contents causing contention and locks in the table. We recommend the use of either Sequence or
Identity based value generators (see below) - which you use would depend on your RDBMS.
For a class using datastore identity you need to add metadata something like the following
To configure an application identity class to use this generation method you simply add this to the
class' JDO Meta-Data.
This value generator will NOT guarantee to generate values unique across different JVMs. This is
because it will select the "max+1" and before creating the record another thread may come in and
insert one.
52.1.9 uuid
This generator uses the JDK1.5 UUID class to generate values. The values are 128-bit (36 character)
of the form "0e400c2c-b3a0-4786-a0c6-f2607bf643eb"
This generator can be used in concurrent applications. It is especially useful in situations where large
numbers of transactions within a certain amount of time have to be made, and the additional overhead
of synchronizing the concurrent creation of unique identifiers through the database would break
performance limits.
For a class using datastore identity you need to add metadata something like the following
To configure an application identity class to use this generation method you simply add this to the
class' JDO Meta-Data.
Or using annotations
This value generator will generate values unique across different JVMs
52.1.10 auid
This generator uses a Java implementation of DCE UUIDs to create unique identifiers without the
overhead of additional database transactions or even an open database connection. The identifiers are
Strings of the form "LLLLLLLL-MMMM-HHHH-CCCC-NNNNNNNNNNNN" where 'L', 'M', 'H',
'C' and 'N' are the DCE UUID fields named time low, time mid, time high, clock sequence and node.
This generator can be used in concurrent applications. It is especially useful in situations where large
numbers of transactions within a certain amount of time have to be made, and the additional overhead
of synchronizing the concurrent creation of unique identifiers through the database would break
performance limits.
For a class using datastore identity you need to add metadata something like the following
To configure an application identity class to use this generation method you simply add this to the
class' JDO Meta-Data.
This value generator will generate values unique across different JVMs
52.1.11 timestamp
This method will create a java.sql.Timestamp of the current time (at insertion in the datastore).
For a class using datastore identity you need to add metadata something like the following
To configure an application identity class to use this generation method you simply add this to the
class' JDO Meta-Data.
52.1.12 timestamp-value
This method will create a long of the current time in millisecs (at insertion in the datastore).
For a class using datastore identity you need to add metadata something like the following
To configure an application identity class to use this generation method you simply add this to the
class' JDO Meta-Data.
This section describes how to use the DataNucleus Value Generator API for generating unique
keys for objects outside the DataNucleus (JDO) runtime. DataNucleus defines a framework for
identity generation and provides many builtin strategies for identities. You can make use of the same
strategies described above but for generating identities manually for your own use. The process is
described below
The DataNucleus Value Generator API revolves around 2 classes. The entry point for retrieving
generators is the ValueGenerationManager. This manages the appropriate ValueGenerator classes.
Value generators maintain a block of cached ids in memory which avoid reading the database each
time it needs a new unique id. Caching a block of unique ids provides you the best performance but
can cause "holes" in the sequence of ids for the stored objects in the database.
Let's take an example. Here we want to obtain an identity using the TableGenerator ("increment"
above). This stores identities in a datastore table. We want to generate an identity using this. Here is
what we add to our code
// Obtain a ValueGenerationManager
ValueGenerationManager mgr = new ValueGenerationManager();
Some ValueGenerators are specific to RDBMS datastores, and some are generic, so bear this in mind
when selecting and adding your own.
53 Sequences
.......................................................................................................................................
. There are 2 forms of "sequence" available through this interface - the ones that DataNucleus
provides utilising datastore capabilities, and ones that a user provides using something known as a
"factory class".
<jdo>
<package name="MyPackage">
<class name="MyClass">
...
</class>
So we have defined two Sequences for the package MyPackage. Each sequence has a symbolic name
that is referred to within JDO (within DataNucleus), and it has a name in the datastore. The final
attribute represents whether the sequence is transactional or not.
All we need to do now is to access the Sequence in our persistence code in our application. This is
done as follows
PersistenceManager pm = pmf.getPersistenceManager();
Please be aware that when you have a Sequence declared with a strategy of "contiguous" this means
"transactional contiguous" and that you need to have a Transaction open when you access it.
JDO3.1 allows control over the allocation size (default=50) and initial value (default=1) for the
sequence. So we can do
which will allocate 10 new sequence values each time the allocated sequence values is exhausted.
So our sequence simply increments by 1 each call to next(). The next thing we need to do is provide a
factory class that creates this Sequence. This factory needs to have a static newInstance method that
returns the Sequence object. We define our factory like this
package org.datanucleus.samples.sequence;
import javax.jdo.datastore.Sequence;
<jdo>
<package name="MyPackage">
<class name="MyClass">
...
</class>
PersistenceManager pm = pmf.getPersistenceManager();
54 Embedded Fields
.......................................................................................................................................
<jdo>
<package name="com.mydomain.samples.embedded">
<class name="MyClass" embedded-only="true">
...
</class>
</package>
</jdo>
With the above MetaData (using the embedded-only attribute), in our application any objects of the
class MyClass cannot be persisted in their own right. They can only be embedded into other objects.
JDO's definition of embedding encompasses several types of fields. These are described below
• Embedded persistable objects - where you have a 1-1 relationship and you want to embed the
other persistable into the same table as the your object.
• Embedded Nested persistable objects - like the first example except that the other object also has
another persistable object that also should be embedded
• Embedded Collection elements - where you want to embed the elements of a collection into a
join table (instead of persisting them into their own table)
• Embedded Map keys/values - where you want to embed the keys/values of a map into a join
table (instead of persisting them into their own table)
Applicable to RDBMS, Excel, OOXML, ODF, HBase, MongoDB, Neo4j, Cassandra, JSON.
In a typical 1-1 relationship between 2 classes, the 2 classes in the relationship are persisted to their
own table, and a foreign key is managed between them. With JDO and DataNucleus you can persist
the related persistable object as embedded into the same table. This results in a single table in the
datastore rather than one for each of the 2 classes.
Let's take an example. We are modelling a Computer, and in our simple model our Computer has
a graphics card and a sound card. So we model these cards using a ComputerCard class. So our
classes become
...
}
...
}
The traditional (default) way of persisting these classes would be to have a table to represent each
class. So our datastore will look like this
However we decide that we want to persist Computer objects into a table called COMPUTER and
we also want to persist the PC cards into the same table. We define our MetaData like this
<jdo>
<package name="com.mydomain.samples.embedded">
<class name="Computer" identity-type="datastore" table="COMPUTER">
<field name="operatingSystem">
<column name="OS_NAME" length="40" jdbc-type="CHAR"/>
</field>
<field name="graphicsCard" persistence-modifier="persistent">
<embedded null-indicator-column="GRAPHICS_MANUFACTURER">
<field name="manufacturer" column="GRAPHICS_MANUFACTURER"/>
<field name="type" column="GRAPHICS_TYPE"/>
</embedded>
</field>
<field name="soundCard" persistence-modifier="persistent">
<embedded null-indicator-column="SOUND_MANUFACTURER">
<field name="manufacturer" column="SOUND_MANUFACTURER"/>
<field name="type" column="SOUND_TYPE"/>
</embedded>
</field>
</class>
You will notice in the MetaData our use of the attribute null-indicator-column. This is used
when retrieving objects from the datastore and detecting if it is a NULL embedded object. In
the case we have here, if the column GRAPHICS_MANUFACTURER is null at retrieval, then
the embedded "graphicsCard" field will be set as null. Similarly for the "soundCard" field when
SOUND_MANUFACTURER is null.
If the ComputerCard class above has a reference back to the related Computer, JDO defines a
mechanism whereby this will be populated. You would add the XML element owner-field to the
<embedded> tag defining the field within ComputerCard that represents the Computer it relates
to. When this is specified DataNucleus will populate it automatically, so that when you retrieve the
Computer and access the ComputerCard objects within it, they will have the link in place.
It should be noted that in this latter (embedded) case we can still persist objects of type
ComputerCard into their own table - the MetaData definition for ComputerCard is used for the
table definition in this case.
Please note that if, instead of specifying the <embedded> block we had specified embedded in the
field element we would have ended up with the same thing, just that the fields and columns would
have been mapped using their default mappings, and that the <embedded> provides control over how
they are mapped.
Note that by default the embedded objects cannot have inheritance. Inheritance in embedded
objects is only support for RDBMS and MongoDB, and involves defining a discriminator in the
metadata of the embedded type.
See also :-
Applicable to RDBMS, Excel, OOXML, ODF, HBase, MongoDB, Neo4j, Cassandra, JSON.
In the above example we had an embedded persistable object within a persisted object. What if our
embedded persistable object also contain another persistable object. So, using the above example what
if ComputerCard contains an object of type Connector ?
Connector connector;
...
}
Well we want to store all of these objects into the same record in the COMPUTER table.
<jdo>
<package name="com.mydomain.samples.embedded">
<class name="Computer" identity-type="datastore" table="COMPUTER">
<field name="operatingSystem">
<column name="OS_NAME" length="40" jdbc-type="CHAR"/>
</field>
<field name="graphicsCard" persistence-modifier="persistent">
<embedded null-indicator-column="GRAPHICS_MANUFACTURER">
<field name="manufacturer" column="GRAPHICS_MANUFACTURER"/>
<field name="type" column="GRAPHICS_TYPE"/>
<field name="connector">
<embedded>
<field name="type" column="GRAPHICS_CONNECTOR_TYPE"/>
</embedded>
</field>
</embedded>
</field>
<field name="soundCard" persistence-modifier="persistent">
<embedded null-indicator-column="SOUND_MANUFACTURER">
<field name="manufacturer" column="SOUND_MANUFACTURER"/>
<field name="type" column="SOUND_TYPE"/>
<field name="connector">
<embedded>
<field name="type" column="SOUND_CONNECTOR_TYPE"/>
</embedded>
</field>
</embedded>
</field>
</class>
So we simply nest the embedded definition of the Connector objects within the embedded definition
of the ComputerCard definitions for Computer. JDO supports this to as many levels as you
require! The Connector objects will be persisted into the GRAPHICS_CONNECTOR_TYPE, and
SOUND_CONNECTOR_TYPE columns in the COMPUTER table.
In a typical 1-N relationship between 2 classes, the 2 classes in the relationship are persisted to their
own table, and either a join table or a foreign key is used to relate them. With JDO and DataNucleus
you have a variation on the join table relation where you can persist the objects of the "N" side into
the join table itself so that they don't have their own identity, and aren't stored in the table for that
class. This is supported in DataNucleus with the following provisos
• You can have inheritance in embedded keys/values and a discriminator is added (you must
define the discriminator in the metadata of the embedded type).
• When retrieving embedded elements, all fields are retrieved in one call. That is, fetch plans are
not utilised. This is because the embedded element has no identity so we have to retrieve all
initially.
It should be noted that where the collection "element" is not persistable or of a "reference" type
(Interface or Object) it will always be embedded, and this functionality here applies to persistable
elements only. DataNucleus doesn't support the embedding of reference type objects currently.
Let's take an example. We are modelling a Network, and in our simple model our Network has
collection of Devices. So we define our classes as
...
}
...
}
We decide that instead of Device having its own table, we want to persist them into the join table
of its relationship with the Network since they are only used by the network itself. We define our
MetaData like this
<jdo>
<package name="com.mydomain.samples.embedded">
<class name="Network" identity-type="datastore" table="NETWORK">
<field name="name">
<column name="NAME" length="40" jdbc-type="VARCHAR"/>
</field>
<field name="devices" persistence-modifier="persistent" table="NETWORK_DEVICES">
<collection element-type="com.mydomain.samples.embedded.Device"/>
<join>
<column name="NETWORK_ID"/>
</join>
<element>
<embedded>
<field name="name">
<column name="DEVICE_NAME" allows-null="true"/>
</field>
<field name="ipAddress">
<column name="DEVICE_IP_ADDR" allows-null="true"/>
</field>
</embedded>
</element>
</field>
</class>
So here we will end up with a table called "NETWORK" with columns "NETWORK_ID",
and "NAME", and a table called "NETWORK_DEVICES" with columns "NETWORK_ID",
"ADPT_PK_IDX", "DEVICE_NAME", "DEVICE_IP_ADDR". When we persist a Network object,
any devices are persisted into the NETWORK_DEVICES table.
Please note that if, instead of specifying the <embedded> block we had specified embedded-element
in the collection element we would have ended up with the same thing, just that the fields and
columns would be mapped using their default mappings, and that the <embedded> provides control
over how they are mapped.
You note that in our example above DataNucleus has added an extra column "ADPT_PK_IDX"
to provide the primary key of the join table now that we're storing the elements as embedded. A
variation on this would have been if we wanted to maybe use the "DEVICE_IP_ADDR" as the other
part of the primary key, in which case the "ADPT_PK_IDX" would not be needed. You would specify
this as follows
This results in the join table only having the columns "NETWORK_ID", "DEVICE_IP_ADDR",
and "DEVICE_NAME", and having a primary key as the composite of "NETWORK_ID" and
"DEVICE_IP_ADDR".
See also :-
In a typical 1-N map relationship between classes, the classes in the relationship are persisted to their
own table, and a join table forms the map linkage. With JDO and DataNucleus you have a variation
on the join table relation where you can persist either the key class or the value class, or both key class
and value class into the join table. This is supported in DataNucleus with the following provisos
• You can have inheritance in embedded keys/values and a discriminator is added (you must
define the discriminator in the metadata of the embedded type).
• When retrieving embedded keys/values, all fields are retrieved in one call. That is, fetch plans
are not utilised. This is because the embedded key/value has no identity so we have to retrieve all
initially.
It should be noted that where the map "key"/"value" is not persistable or of a "reference" type
(Interface or Object) it will always be embedded, and this functionality here applies to persistable
keys/values only. DataNucleus doesn't support embedding reference type elements currently.
Let's take an example. We are modelling a FilmLibrary, and in our simple model our FilmLibrary
has map of Films, keyed by a String alias. So we define our classes as
...
}
...
}
We decide that instead of Film having its own table, we want to persist them into the join table of its
map relationship with the FilmLibrary since they are only used by the library itself. We define our
MetaData like this
<jdo>
<package name="com.mydomain.samples.embedded">
<class name="FilmLibrary" identity-type="datastore" table="FILM_LIBRARY">
<field name="owner">
<column name="OWNER" length="40" jdbc-type="VARCHAR"/>
</field>
<field name="films" persistence-modifier="persistent" table="FILM_LIBRARY_FILMS">
<map/>
<join>
<column name="FILM_LIBRARY_ID"/>
</join>
<key>
<column name="FILM_ALIAS"/>
</key>
<value>
<embedded>
<field name="name">
<column name="FILM_NAME"/>
</field>
<field name="director">
<column name="FILM_DIRECTOR" allows-null="true"/>
</field>
</embedded>
</value>
</field>
</class>
So here we will end up with a table called "FILM_LIBRARY" with columns "FILM_LIBRARY_ID",
and "OWNER", and a table called "FILM_LIBRARY_FILMS" with columns
"FILM_LIBRARY_ID", "FILM_ALIAS", "FILM_NAME", "FILM_DIRECTOR". When we persist a
FilmLibrary object, any films are persisted into the FILM_LIBRARY_FILMS table.
Please note that if, instead of specifying the <embedded> block we had specified embedded-key
of embedded-value in the map element we would have ended up with the same thing, just that the
fields and columns would be mapped using their default mappings, and that the <embedded> provides
control over how they are mapped.
See also :-
55 Serialised Fields
.......................................................................................................................................
• Serialised Array fields - where you want to serialise the array into a single "BLOB" column.
• Serialised Collection fields - where you want to serialise the collection into a single "BLOB"
column.
• Serialised Collection elements - where you want to serialise the collection elements into a single
column in a join table.
• Serialised Map fields - where you want to serialise the map into a single "BLOB" column
• Serialised Map keys/values - where you want to serialise the map keys and/or values into single
column(s) in a join table.
• Serialised persistable fields - where you want to serialise a PC object into a single "BLOB"
column.
• Serialised Reference (Interface/Object) fields - where you want to serialise a reference field into
a single "BLOB" column.
• Serialised field to local disk - not part of the JDO spec but available as an option for RDBMS
datastores usage
Perhaps the most important thing to bear in mind when deciding to serialise a field is that that object
must implement java.io.Serializable.
Collections are usually persisted by way of either a join table, or by use of a foreign-key in the
element table. In some situations it is required to store the whole collection in a single column in the
table of the class being persisted. This prohibits the querying of such a collection, but will persist the
collection in a single statement. Let's take an example. We have the following classes
and we want the animals collection to be serialised into a single column in the table storing the Farm
class, so we define our MetaData like this
So we make use of the serialized attribute of <field>. This specification results in a table like this
Applicable to RDBMS
Collections are usually persisted by way of either a join table, or by use of a foreign-key in the
element table. In some situations you may want to serialise the element into a single column in the
join table. Let's take an example. We have the same classes as in the previous case and we want the
animals collection to be stored in a join table, and the element serialised into a single column storing
the "Animal" object. We define our MetaData like this
So we make use of the serialized-element attribute of <collection>. This specification results in tables
like this
Maps are usually persisted by way of a join table, or very occasionaly using a foreign-key in the value
table. In some situations it is required to store the whole map in a single column in the table of the
class being persisted. This prohibits the querying of such a map, but will persist the map in a single
statement. Let's take an example. We have the following classes
and we want the children map to be serialised into a single column in the table storing the ClassRoom
class, so we define our MetaData like this
<class name="ClassRoom">
<field name="level">
<column name="LEVEL"/>
</field>
<field name="children" serialized="true">
<map key-type="java.lang.String" value-type="Child"/>
<column name="CHILDREN"/>
</field>
</class>
<class name="Child"/>
So we make use of the serialized attribute of <field>. This specification results in a table like this
• Map<non- persistable, non- persistable>, and no <join> is specified. Since the keys/values
don't have a table of their own, the only option is to serialise the whole map and it appears as a
single BLOB field in the table of the main class.
• Map<non- persistable, persistable>, with "embedded-value" set to true and no <join> is
specified. Since the keys/values are embedded and there is no join table, then the whole map is
serialised as above.
See also :-
Applicable to RDBMS
Maps are usually persisted by way of a join table, or very occasionaly using a foreign-key in the value
table. In the join table case you have the option of serialising the keys and/or the values each into a
single (BLOB) column in the join table. This is performed in a similar way to serialised elements for
collections, but this time using the "serialized-key", "serialized-value" attributes. We take the example
in the previous section, with "a classroom of children" and the children stored in a map field. This
time we want to serialise the child object into the join table of the map
<class name="ClassRoom">
<field name="level">
<column name="LEVEL"/>
</field>
<field name="children" table="CLASS_CHILDREN">
<map key-type="java.lang.String" value-type="Child" serialized-value="true"/>
<join column="CLASSROOM_ID"/>
<key column="ALIAS"/>
<value column="CHILD"/>
</field>
</class>
<class name="Child"/>
So we make use of the serialized-value attribute of <map>. This results in a schema like this
A field that is a persistable object is typically stored as a foreign-key relation between the container
object and the contained object. In some situations it is not necessary that the contained object has
an identity of its own, and for efficiency of access the contained object is required to be stored in a
BLOB column in the containing object's datastore table. Let's take an example. We have the following
classes
and we want the teacher object to be serialised into a single column in the table storing the
ClassRoom class, so we define our MetaData like this
<class name="ClassRoom">
<field name="level">
<column name="LEVEL"/>
</field>
<field name="teacher" serialized="true">
<column name="TEACHER"/>
</field>
</class>
So we make use of the serialized attribute of <field>. This specification results in a table like this
Applicable to RDBMS
A reference (Interface/Object) field is typically stored as a foreign-key relation between the container
object and the contained implementation of the reference. In some situations it is not necessary that
the contained object has an identity of its own, and for efficiency of access the contained object
is required to be stored in a BLOB column in the containing object's datastore table. Let's take an
example using an interface field. We have the following classes
and we want the teacher object to be serialised into a single column in the table storing the
ClassRoom class, so we define our MetaData like this
<class name="ClassRoom">
<field name="level">
<column name="LEVEL"/>
</field>
<field name="teacher" serialized="true">
<column name="TEACHER"/>
</field>
</class>
<class name="Teacher">
</class>
So we make use of the serialized attribute of <field>. This specification results in a table like this
Applicable to RDBMS
If you have a non-relation field that implements Serializable you have the option of serialising it into
a file on the local disk. This could be useful where you have a large file and don't want to persist very
large objects into your RDBMS. Obviously this will mean that the field is no longer queryable, but
then if its a large file you likely don't care about that. So let's give an example
@PersistenceCapable
public class Person
{
@PrimaryKey
long id;
@Persistent
@Extension(vendorName="datanucleus", key="serializeToFileLocation"
value="person_avatars")
AvatarImage image;
}
Or using XML
<class name="Person">
...
<field name="image" persistence-modifier="persistent">
<extension vendor-name="datanucleus" key="serializeToFileLocation"
value="person_avatars"/>
</field>
</class>
So this will now persist a file into a folder person_avatars with filename as the String form of the
identity of the owning object. In a real world example you likely will specify the extension value as an
absolute path name, so you can place it anywhere in the local disk.
56 Interface Fields
.......................................................................................................................................
You then have a class that contains an object of this interface type
JDO doesn't define how an interface is persisted in the datastore. Obviously there can be many
implementations and so no obvious solution. DataNucleus allows the following
• per-implementation : a FK is created for each implementation so that the datastore can provide
referential integrity. The other advantage is that since there are FKs then querying can be
performed. The disadvantage is that if there are many implementations then the table can become
large with many columns not used
• identity : a single column is added and this stores the class name of the implementation
stored, as well as the identity of the object. The advantage is that if you have large numbers
of implementations then this can cope with no schema change. The disadvantages are that no
querying can be performed, and that there is no referential integrity.
• xcalia : a slight variation on "identity" whereby there is a single column yet the contents of that
column are consistent with what Xcalia XIC JDO implementation stored there.
The user controls which one of these is to be used by specifying the extension mapping-strategy on
the field containing the interface. The default is "per-implementation"
In terms of the implementations of the interface, you can either leave the field to accept any
known about implementation, or you can restrict it to only accept some implementations (see
"implementation-classes" metadata extension). If you are leaving it to accept any persistable
implementation class, then you need to be careful that such implementations are known to
DataNucleus at the point of encountering the interface field. By this we mean, DataNucleus has to
have encountered the metadata for the implementation so that it can allow for the implementation
when handling the field. You can force DataNucleus to know about a persistable class by using an
autostart mechanism, or using persistence.xml, or by placement of the package.jdo file so that when
the owning class for the interface field is encountered so is the metadata for the implementations.
56.1.1 1-1
To allow persistence of this interface field with DataNucleus you have 2 levels of control. The first
level is global control. Since all of our Square, Circle, Rectangle classes implement Shape then we
just define them in the MetaData as we would normally.
<package name="mydomain">
<class name="Square">
...
</class>
<class name="Circle">
...
</class>
<class name="Rectangle">
...
</class>
</package>
The global way means that when mapping that field DataNucleus will look at all persistable classes it
knows about that implement the specified interface.
JDO also allows users to specify a list of classes implementing the interface on a field-by-field basis,
defining which of these implementations are accepted for a particular interface field. To do this you
define the Meta-Data like this
<package name="mydomain">
<class name="ShapeHolder">
<field name="shape" persistence-modifier="persistent"
field-type="mydomain.Circle,mydomain.Rectangle,mydomain.Square"/>
</class>
That is, for any interface object in a class to be persisted, you define the possible implementation
classes that can be stored there. DataNucleus interprets this information and will map the above
example classes to the following in the database
So DataNucleus adds foreign keys from the containers table to all of the possible implementation
tables for the shape field.
If we use mapping-strategy of "identity" then we get a different datastore schema.
<class name="ShapeHolder">
<field name="shape" persistence-modifier="persistent">
<extension vendor-name="datanucleus" key="mapping-strategy" value="identity"/>
</field>
</class>
and the column "SHAPE" will contain strings such as mydomain.Circle:1 allowing retrieval of the
related implementation object.
56.1.2 1-N
You can have a Collection/Map containing elements of an interface type. You specify this in the same
way as you would any Collection/Map. You can have a Collection of interfaces as long as you use
a join table relation and it is unidirectional. The "unidirectional" restriction is that the interface
is not persistent on its own and so cannot store the reference back to the owner object. Use the 1-N
relationship guides for the metadata definition to use.
You need to use a DataNucleus extension tag "implementation-classes" if you want to restrict the
collection to only contain particular implementations of an interface. For example
<class name="ShapeHolder">
<field name="shapes" persistence-modifier="persistent">
<collection element-type="mydomain.Shape"/>
<join/>
<extension vendor-name="datanucleus" key="implementation-classes"
value="mydomain.Circle,mydomain.Rectangle,mydomain.Square,mydomain.Triangle"/>
</field>
</class>
So the shapes field is a Collection of mydomain.Shape and it will accept the implementations of type
Circle, Rectangle, Square and Triangle. If you omit the implementation-classes tag then you have
to give DataNucleus a way of finding the metadata for the implementations prior to encountering this
field.
57 Object Fields
.......................................................................................................................................
57.1.1 FCO
Let's suppose you have a field in a class and you have a selection of possible persistable class that
could be stored there, so you decide to make the field a java.lang.Object. So let's take an example. We
have the following class
So we have a space in a car park, and in that space we have an occupier of the space. We have
some legacy data and so can't make the type of this "occupier" an interface type, so we just use
java.lang.Object. Now we know that we can only have particular types of objects stored there (since
there are only a few types of vehicle that can enter the car park). So we define our MetaData like this
<package name="mydomain.samples.object">
<class name="ParkingSpace">
<field name="location"/>
<field name="occupier" persistence-modifier="persistent"
field-type="mydomain.samples.vehicles.Car,
mydomain.samples.vehicles.Motorbike"/>
</field>
</class>
or using annotations
@Persistent(types={mydomain.samples.vehicles.Car.class, mydomain.samples.vehicles.Motorbike.class})
Object occupier;
So DataNucleus adds foreign keys from the ParkingSpace table to all of the possible implementation
tables for the occupier field.
In conclusion, when using "per-implementation" mapping for any java.lang.Object field in a class to
be persisted (as non-serialised), you must define the possible "implementation" classes that can be
stored there.
If we use mapping-strategy of "identity" then we get a different datastore schema.
<class name="ParkingSpace">
<field name="location"/>
<field name="occupier" persistence-modifier="persistent">
<extension vendor-name="datanucleus" key="mapping-strategy" value="identity"/>
</field>
</class>
<class name="MyClass">
<field name="myObject" serialized="true"/>
</class>
Similarly, where you have a collection of Objects using a join table, the objects are, by default, stored
in the table of the persistable instance. If instead you want them to occupy a single BLOB column of
the join table, you should specify the "embedded-element" attribute of <collection> like this
<class name="MyClass">
<field name="myCollection">
<collection element-type="java.lang.Object" serialized-element="true"/>
<join/>
</field>
</class>
Please refer to the serialised fields guide for more details of storing objects in this way.
58 Array Fields
.......................................................................................................................................
• Single Column - the array is byte-streamed into a single column in the table of the containing
object.
• Serialised - the array is serialised into single column in the table of the containing object.
• Using a Join Table - where the array relation is persisted into the join table, with foreign-key
links to an element table where the elements of the array are persistable
• Using a Foreign-Key in the element - only available where the array is of a persistable type
JDO has no simple way of detecting changes to an arrays contents. To update an array you must either
So we have an Account and it has a number of permissions, each expressed as a byte. We want
to persist the permissions in a single-column into the table of the account (but we don't want them
serialised). We then define MetaData something like this
You could have added <array> to be explicit but the type of the field is an array, and the type
declaration also defines the component type so nothing more is needed. This results in a datastore
schema as follows
DataNucleus supports persistence of the following array types in this way : boolean[], byte[], char[],
double[], float[], int[], long[], short[], Boolean[], Byte[], Character[], Double[], Float[], Integer[],
Long[], Short[], BigDecimal[], BigInteger[]
See also :-
So we have an Account and it has a number of permissions, each expressed as a byte. We want to
persist the permissions as serialised into the table of the account. We then define MetaData something
like this
That is, you define the field as serialized. To define arrays of short, long, int, or indeed any other
supported array type you would do the same as above. This results in a datastore schema as follows
DataNucleus supports persistence of many array types in this way, including : boolean[], byte[],
char[], double[], float[], int[], long[], short[], Boolean[], Byte[], Character[], Double[], Float[],
Integer[], Long[], Short[], BigDecimal[], BigInteger[], String[], java.util.Date[], java.util.Locale[]
See also :-
So an Account has an array of Permissions, and both of these objects are persistable. We want to
persist the relationship using a join table. We define the MetaData as follows
See also :-
• MetaData reference for <array> element
• MetaData reference for <element> element
• MetaData reference for <join> element
• MetaData reference for <order> element
• Annotations reference for @Element
• Annotations reference for @Order
So an Account has an array of Permissions, and both of these objects are persistable. We want
to persist the relationship using a foreign-key in the table for the Permission class. We define the
MetaData as follows
See also :-
59 1-to-1 Relations
.......................................................................................................................................
59.1.1 Unidirectional
For this case you could have 2 classes, User and Account, as below.
so the Account class knows about the User class, but not vice-versa. If you define the XML metadata
for these classes as follows
<package name="mydomain">
<class name="User" table="USER">
<field name="id" primary-key="true">
<column name="USER_ID"/>
</field>
...
</class>
@Column(name="USER_ID")
User user;
}
This will create 2 tables in the database, one for User (with name USER), and one for Account (with
name ACCOUNT and a column USER_ID), as shown below.
Things to note :-
• Account has the object reference (and so owns the relation) to User and so its table holds the
foreign-key
• If you call PM.deletePersistent() on the end of a 1-1 unidirectional relation without the relation
and that object is related to another object, an exception will typically be thrown (assuming
the RDBMS supports foreign keys). To delete this record you should remove the other objects
association first.
• If you invoke an operation that will retrieve the one-to-one field, and you only want it to get the
foreign key value (and not join to the related table) you can add the metadata extension fetch-fk-
only (set to "true") to the field/property.
59.1.2 Bidirectional
For this case you could have 2 classes, User and Account again, but this time as below. Here the
Account class knows about the User class, and also vice-versa.
Here we create the 1-1 relationship with a single foreign-key. To do this you define the XML
metadata as
<package name="mydomain">
<class name="User" table="USER">
<field name="id" primary-key="true">
<column name="USER_ID"/>
</field>
...
<field name="account" mapped-by="user"/>
</class>
@Column(name="USER_ID")
User user;
}
@Persistent(mappedBy="user")
Account account;
}
The difference is that we added mapped-by to the field of User. This represents the bidirectionality.
This will create 2 tables in the database, one for User (with name USER), and one for Account (with
name ACCOUNT). With RDBMS the ACCOUNT table will have a column USER_ID (since RDBMS
will place the FK on the side without the "mapped-by"). Like this
With non-RDBMS datastores both tables will have a column containing the "id" of the related object,
that is USER will have an ACCOUNT column, and ACCOUNT will have a USER_ID column.
Things to note :-
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
• If you invoke an operation that will retrieve the one-to-one field (of the non-owner side), and
you only want it to get the foreign key value (and not join to the related table) you can add the
metadata extension fetch-fk-only (set to "true") to the field/property.
59.1.3 Embedded
The above 2 relationship types assume that both classes in the 1-1 relation will have their own table.
You can, of course, embed the elements of one class into the table of the other. This is described in
Embedded PC Objects.
60 1-to-N Relations
.......................................................................................................................................
61 Collections
.......................................................................................................................................
There are 2 ways that we can persist this relationship. These are shown below
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses" table="ACCOUNT_ADDRESSES">
<collection element-type="com.mydomain.Address"/>
<join column="ACCOUNT_ID_OID"/>
<element column="ADDRESS_ID_EID"/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
</class>
</package>
@Persistent(table="ACCOUNT_ADDRESSES")
@Join(column="ACCOUNT_ID_OID")
@Element(column="ADDRESS_ID_EID")
Collection<Address> addresses;
}
The crucial part is the join element on the field element - this signals to JDO to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the name of the join table, specify the table attribute on the field element with the
collection.
• To specify the names of the join table columns, use the column attribute of join, element
elements.
• To specify the foreign-key between container table and join table, specify <foreign-key> below
the <join> element.
• To specify the foreign-key between join table and element table, specify <foreign-key> below
either the <field> element or the <element> element.
• If you wish to share the join table with another relation then use the DataNucleus "shared join
table" extension
• The join table will, by default, be given a primary key. If you want to omit this then you can turn
it off using the DataNucleus metadata extension "primary-key" (within <join>) set to false.
• The column "ADPT_PK_IDX" is added by DataNucleus so that duplicates can be stored. You
can control this by adding an <order> element and specifying the column name for the order
column, or you can override the default naming of this column by specifying the DataNucleus
extension "adapter-column-name" (within <field>).
• If you want the set to include nulls, you can turn on this behaviour by adding the DataNucleus
extension metadata "allow-nulls" to the <field> set to true
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses">
<collection element-type="com.mydomain.Address"/>
<element column="ACCOUNT_ID"/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
</class>
</package>
@Element(column="ACCOUNT_ID")
Collection<Address> addresses;
}
Again there will be 2 tables, one for Address, and one for Account. Note that we have no "mapped-
by" attribute specified, and also no "join" element. If you wish to specify the names of the columns
used in the schema for the foreign key in the Address table you should use the element element within
the field of the collection.
In terms of operation within your classes of assigning the objects in the relationship. You have to take
your Account object and add the Address to the Account collection field since the Address knows
nothing about the Account.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the foreign-key between container table and element table, specify <foreign-key>
below either the <field> element or the <element> element.
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this
mode of persistence will not allow duplicate values in the Collection. If you want to allow duplicate
Collection entries, then use the "Join Table" variant above.
There are 2 ways that we can persist this relationship. These are shown below
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses" mapped-by="account">
<collection element-type="com.mydomain.Address"/>
<join/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
<field name="account"/>
</class>
</package>
@Persistent(mappedBy="account")
@Join
Collection<Address> addresses;
}
The crucial part is the join element on the field element - this signals to JDO to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the name of the join table, specify the table attribute on the field element with the
collection.
• To specify the names of the join table columns, use the column attribute of join, element
elements.
• To specify the foreign-key between container table and join table, specify <foreign-key> below
the <join> element.
• To specify the foreign-key between join table and element table, specify <foreign-key> below
either the <field> element or the <element> element.
• If you wish to share the join table with another relation then use the DataNucleus "shared join
table" extension
• The join table will, by default, be given a primary key. If you want to omit this then you can turn
it off using the DataNucleus metadata extension "primary-key" (within <join>) set to false.
• The column "ADPT_PK_IDX" is added by DataNucleus so that duplicates can be stored. You
can control this by adding an <order> element and specifying the column name for the order
column, or you can override the default naming of this column by specifying the DataNucleus
extension "adapter-column-name" (within <field>).
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
• If you want the set to include nulls, you can turn on this behaviour by adding the extension
metadata "allow-nulls" to the <field> set to true
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses" mapped-by="account">
<collection element-type="com.mydomain.Address"/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
<field name="account">
<column name="ACCOUNT_ID"/>
</field>
</class>
</package>
@Persistent(mappedBy="account")
Collection<Address> addresses;
}
The crucial part is the mapped-by on the "1" side of the relationship. This tells the JDO implementation to look
for a field called account on the Address class.
This will create 2 tables in the database, one for Address (including an ACCOUNT_ID to link to the
ACCOUNT table), and one for Account. Notice the subtle difference to this set-up to that of the Join
Table relationship earlier.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the foreign-key between container table and element table, specify <foreign-key>
below either the <field> element or the <element> element.
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this
mode of persistence will not allow duplicate values in the Collection. If you want to allow duplicate
Collection entries, then use the "Join Table" variant above.
All of the examples above show a 1-N relationship between 2 persistable classes. DataNucleus can
also cater for a Collection of primitive or Object types. For example, when you have a Collection of
Strings. This will be persisted in the same way as the "Join Table" examples above. A join table is
created to hold the collection elements. Let's take our example. We have an Account that stores a
Collection of addresses. These addresses are simply Strings. We define the XML metadata like this
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses" persistence-modifier="persistent">
<collection element-type="java.lang.String"/>
<join/>
<element column="ADDRESS"/>
</field>
</class>
@Persistent
@Join
@Element(column="ADDRESS")
Collection<String> addresses;
}
The ACCOUNT table is as before, but this time we only have the "join table". In our MetaData we
used the <element> tag to specify the column name to use for the actual address String.
Please note that the column ADPT_PK_IDX is added by DataNucleus so that duplicates can be
stored. You can override the default naming of this column by specifying the DataNucleus extension
"adapter-column-name" within the <field> for the Collection.
The relationships using join tables shown above rely on the join table relating to the relation in
question. DataNucleus allows the possibility of sharing a join table between relations. The example
below demonstrates this. We take the example as show above (1-N Unidirectional Join table
relation), and extend Account to have 2 collections of Address records. One for home addresses and
one for work addresses, like this
We now change the metadata we had earlier to allow for 2 collections, but sharing the join table
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="workAddresses" persistence-modifier="persistent" table="ACCOUNT_ADDRESSES">
<collection element-type="com.mydomain.Address"/>
<join column="ACCOUNT_ID_OID"/>
<element column="ADDRESS_ID_EID"/>
<extension vendor-name="datanucleus" key="relation-discriminator-column" value="ADDRESS_TYPE"
<extension vendor-name="datanucleus" key="relation-discriminator-pk" value="true"/>
<extension vendor-name="datanucleus" key="relation-discriminator-value" value="work"/>
</field>
<field name="homeAddresses" persistence-modifier="persistent" table="ACCOUNT_ADDRESSES">
<collection element-type="com.mydomain.Address"/>
<join column="ACCOUNT_ID_OID"/>
<element column="ADDRESS_ID_EID"/>
<extension vendor-name="datanucleus" key="relation-discriminator-column" value="ADDRESS_TYPE"
<extension vendor-name="datanucleus" key="relation-discriminator-pk" value="true"/>
<extension vendor-name="datanucleus" key="relation-discriminator-value" value="home"/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
</class>
</package>
So we have defined the same join table for the 2 collections "ACCOUNT_ADDRESSES", and the
same columns in the join table, meaning that we will be sharing the same join table to represent
both relations. The important step is then to define the 3 DataNucleus extension tags. These define a
column in the join table (the same for both relations), and the value that will be populated when a row
of that collection is inserted into the join table. In our case, all "home" addresses will have a value of
"home" inserted into this column, and all "work" addresses will have "work" inserted. This means we
can now identify easily which join table entry represents which relation field.
This results in the following database schema
The relationships using foreign keys shown above rely on the foreign key relating to the relation
in question. DataNucleus allows the possibility of sharing a foreign key between relations between
the same classes. The example below demonstrates this. We take the example as show above (1-N
Unidirectional Foreign Key relation), and extend Account to have 2 collections of Address records.
One for home addresses and one for work addresses, like this
We now change the metadata we had earlier to allow for 2 collections, but sharing the join table
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="workAddresses" persistence-modifier="persistent">
<collection element-type="com.mydomain.Address"/>
<element column="ACCOUNT_ID_OID"/>
<extension vendor-name="datanucleus" key="relation-discriminator-column" value="ADDRESS_TYPE"
<extension vendor-name="datanucleus" key="relation-discriminator-value" value="work"/>
</field>
<field name="homeAddresses" persistence-modifier="persistent">
<collection element-type="com.mydomain.Address"/>
<element column="ACCOUNT_ID_OID"/>
<extension vendor-name="datanucleus" key="relation-discriminator-column" value="ADDRESS_TYPE"
<extension vendor-name="datanucleus" key="relation-discriminator-value" value="home"/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
</class>
</package>
So we have defined the same foreign key for the 2 collections "ACCOUNT_ID_OID", The important
step is then to define the 2 DataNucleus extension tags. These define a column in the element table
(the same for both relations), and the value that will be populated when a row of that collection is
inserted into the element table. In our case, all "home" addresses will have a value of "home" inserted
into this column, and all "work" addresses will have "work" inserted. This means we can now identify
easily which element table entry represents which relation field.
This results in the following database schema
62 Sets
.......................................................................................................................................
There are 2 ways that we can persist this relationship. These are shown below
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses">
<collection element-type="com.mydomain.Address"/>
<join/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
</class>
</package>
@Join
Set<Address> addresses;
}
The crucial part is the join element on the field element - this signals to JDO to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the name of the join table, specify the table attribute on the field element with the
collection.
• To specify the names of the join table columns, use the column attribute of join, element
elements.
• To specify the foreign-key between container table and join table, specify <foreign-key> below
the <join> element.
• To specify the foreign-key between join table and element table, specify <foreign-key> below
either the <field> element or the <element> element.
• If you wish to share the join table with another relation then use the DataNucleus "shared join
table" extension
• The join table will, by default, be given a primary key. If you want to omit this then you can turn
it off using the DataNucleus metadata extension "primary-key" (within <join>) set to false.
• If you want the set to include nulls, you can turn on this behaviour by adding the extension
metadata "allow-nulls" to the <field> set to true
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses">
<collection element-type="com.mydomain.Address"/>
<element column="ACCOUNT_ID"/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
</class>
</package>
@Element(column="ACCOUNT_ID")
Set<Address> addresses;
}
Again there will be 2 tables, one for Address, and one for Account. Note that we have no "mapped-
by" attribute specified, and also no "join" element. If you wish to specify the names of the columns
used in the schema for the foreign key in the Address table you should use the element element within
the field of the collection.
In terms of operation within your classes of assigning the objects in the relationship. You have to take
your Account object and add the Address to the Account collection field since the Address knows
nothing about the Account. Also be aware that each Address object can have only one owner, since it
has a single foreign key to the Account. If you wish to have an Address assigned to multiple Accounts
then you should use the "Join Table" relationship above.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the foreign-key between container table and element table, specify <foreign-key>
below either the <field> element or the <element> element.
There are 2 ways that we can persist this relationship. These are shown below
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses" mapped-by="account">
<collection element-type="com.mydomain.Address"/>
<join/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
<field name="account"/>
</class>
</package>
@Persistent(mappedBy="account")
@Join
Set<Address> addresses;
}
Account account;
}
The crucial part is the join element on the field element - this signals to JDO to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the name of the join table, specify the table attribute on the field element with the
collection.
• To specify the names of the join table columns, use the column attribute of join, element
elements.
• To specify the foreign-key between container table and join table, specify <foreign-key> below
the <join> element.
• To specify the foreign-key between join table and element table, specify <foreign-key> below
either the <field> element or the <element> element.
• If you wish to share the join table with another relation then use the DataNucleus "shared join
table" extension
• The join table will, by default, be given a primary key. If you want to omit this then you can turn
it off using the DataNucleus metadata extension "primary-key" (within <join>) set to false.
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
• If you want the set to include nulls, you can turn on this behaviour by adding the extension
metadata "allow-nulls" to the <field> set to true
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses" mapped-by="account">
<collection element-type="com.mydomain.Address"/>
</field>
</class>
<class name="Address">
<field name="id" primary-key="true">
<column name="ADDRESS_ID"/>
</field>
...
<field name="account">
<column name="ACCOUNT_ID"/>
</field>
</class>
</package>
@Persistent(mappedBy="account")
Set<Address> addresses;
}
Account account;
}
The crucial part is the mapped-by attribute of the field on the "1" side of the relationship. This tells the JDO
implementation to look for a field called account on the Address class.
This will create 2 tables in the database, one for Address (including an ACCOUNT_ID to link to the
ACCOUNT table), and one for Account. Notice the subtle difference to this set-up to that of the Join
Table relationship earlier.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the foreign-key between container table and element table, specify <foreign-key>
below either the <field> element or the <element> element.
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
All of the examples above show a 1-N relationship between 2 persistable classes. DataNucleus can
also cater for a Collection of primitive or Object types. For example, when you have a Collection of
Strings. This will be persisted in the same way as the "Join Table" examples above. A join table is
created to hold the collection elements. Let's take our example. We have an Account that stores a
Collection of addresses. These addresses are simply Strings. We define the Meta-Data like this
<package name="com.mydomain">
<class name="Account">
<field name="id" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
...
<field name="addresses" persistence-modifier="persistent">
<collection element-type="java.lang.String"/>
<join/>
<element column="ADDRESS"/>
</field>
</class>
@Join
@Element(column="ADDRESS")
Set<String> addresses;
}
The ACCOUNT table is as before, but this time we only have the "join table". In our MetaData we
used the <element> tag to specify the column name to use for the actual address String.
Please note that the column ADPT_PK_IDX is added by DataNucleus when the column type of
the element is not valid to be part of a primary key (with the RDBMS being used). If the column
type of your element is acceptable for use as part of a primary key then you will not have this
"ADPT_PK_IDX" column. You can override the default naming of this column by specifying the
DataNucleus extension "adapter-column-name" within the <field> for the Collection.
63 Lists
.......................................................................................................................................
There are 2 ways that we can persist this relationship. These are shown below
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses">
<collection element-type="com.mydomain.Address"/>
<join/>
</field>
</class>
@Join
List<Address> addresses;
}
The crucial part is the join element on the field element - this signals to JDO to use a join table.
There will be 3 tables, one for Address, one for Account, and the join table. The difference from Set
is in the contents of the join table. An index column (INTEGER_IDX) is added to keep track of the
position of objects in the List. The name of this column can be controlled using the <order> MetaData
element.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the name of the join table, specify the table attribute on the field element with the
collection.
• To specify the names of the join table columns, use the column attribute of join, element and
order elements.
• To specify the foreign-key between container table and join table, specify <foreign-key> below
the <join> element.
• To specify the foreign-key between join table and element table, specify <foreign-key> below
either the <field> element or the <element> element.
• If you wish to share the join table with another relation then use the DataNucleus "shared join
table" extension
• The join table will, by default, be given a primary key. If you want to omit this then you can turn
it off using the DataNucleus metadata extension "primary-key" (within <join>) set to false.
• The column "ADPT_PK_IDX" is added by DataNucleus so that duplicates can be stored. You
can control this by adding an <order> element and specifying the column name for the order
column, or you can override the default naming of this column by specifying the DataNucleus
extension "adapter-column-name" (within <field>).
• If you want the set to include nulls, you can turn on this behaviour by adding the extension
metadata "allow-nulls" to the <field> set to true
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses">
<collection element-type="com.mydomain.Address"/>
<element column="ACCOUNT_ID"/>
</field>
</class>
@Element(column="ACCOUNT_ID")
List<Address> addresses;
}
Again there will be 2 tables, one for Address, and one for Account. Note that we have no "mapped-
by" attribute specified, and also no "join" element. If you wish to specify the names of the columns
used in the schema for the foreign key in the Address table you should use the element element within
the field of the collection.
In terms of operation within your classes of assigning the objects in the relationship. With
DataNucleus and List-based containers you have to take your Account object and add the Address to
the Account collection field since the Address knows nothing about the Account.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the foreign-key between container table and element table, specify <foreign-key>
below either the <field> element or the <element> element.
Limitations
• Since each Address object can have at most one owner (due to the "Foreign Key") this mode of
persistence will not allow duplicate values in the List. If you want to allow duplicate List entries,
then use the "Join Table" variant above.
This is the same as the case above except that we don't want an indexing column adding to the
element and instead we define an "ordering" criteria. This is a DataNucleus extension to JDO. So we
define the XML metadata like this
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses">
<collection element-type="com.mydomain.Address"/>
<order>
<extension vendor-name="datanucleus" key="list-ordering" value="city ASC"/>
</order>
</field>
</class>
As above there will be 2 tables, one for Address, and one for Account. We have no indexing column,
but instead we will order the elements using the "city" field in ascending order.
In terms of operation within your classes of assigning the objects in the relationship. With
DataNucleus and List-based containers you have to take your Account object and add the Address to
the Account collection field since the Address knows nothing about the Account.
Limitations
• Ordered lists are only ordered in the defined way when retrieved from the datastore.
There are 2 ways that we can persist this relationship. These are shown below
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses" mapped-by="account">
<collection element-type="com.mydomain.Address"/>
<join/>
</field>
</class>
@Persistent(mappedBy="account")
@Join
List<Address> addresses;
}
Account account;
}
The crucial part is the join element on the field element - this signals to JDO to use a join table.
There will be 3 tables, one for Address, one for Account, and the join table. The difference from Set
is in the contents of the join table. An index column (INTEGER_IDX) is added to keep track of the
position of objects in the List. The name of this column can be controlled using the <order> MetaData
element.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the name of the join table, specify the table attribute on the field element with the
collection.
• To specify the names of the join table columns, use the column attribute of join, element and
order elements.
• To specify the foreign-key between container table and join table, specify <foreign-key> below
the <join> element.
• To specify the foreign-key between join table and element table, specify <foreign-key> below
either the <field> element or the <element> element.
• If you wish to share the join table with another relation then use the DataNucleus "shared join
table" extension
• The join table will, by default, be given a primary key. If you want to omit this then you can turn
it off using the DataNucleus metadata extension "primary-key" (within <join>) set to false.
• The column "ADPT_PK_IDX" is added by DataNucleus so that duplicates can be stored. You
can control this by adding an <order> element and specifying the column name for the order
column, or you can override the default naming of this column by specifying the DataNucleus
extension "adapter-column-name" (within <field>).
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
• If you want the set to include nulls, you can turn on this behaviour by adding the extension
metadata "allow-nulls" to the <field> set to true
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses" mapped-by="account">
<collection element-type="com.mydomain.Address"/>
</field>
</class>
@Persistent(mappedBy="account")
List<Address> addresses;
}
Account account;
}
The crucial part is the mapped-by attribute of the field on the "1" side of the relationship. This tells the JDO
implementation to look for a field called account on the Address class.
Again there will be 2 tables, one for Address, and one for Account. The difference from the Set
example is that the List index is placed in the table for Address whereas for a Set this is not needed.
In terms of operation within your classes of assigning the objects in the relationship. With
DataNucleus and List-based containers you have to take your Account object and add the
Address to the Account collection field (you can't just take the Address object and set its
Account field since the position of the Address in the List needs setting, and this is done by
adding the Address to the Account). In addition, if you are removing an object from a List, you
cannot simply set the owner on the element to "null". You have to remove it from the List end
of the relationship.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the foreign-key between container table and element table, specify <foreign-key>
below either the <field> element or the <element> element.
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this
mode of persistence will not allow duplicate values in the List. If you want to allow duplicate List
entries, then use the "Join Table" variant above.
All of the examples above show a 1-N relationship between 2 persistable classes. DataNucleus can
also cater for a List of primitive or Object types. For example, when you have a List of Strings. This
will be persisted in the same way as the "Join Table" examples above. A join table is created to hold
the list elements. Let's take our example. We have an Account that stores a List of addresses. These
addresses are simply Strings. We define the XML metadata like this
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses" persistence-modifier="persistent">
<collection element-type="java.lang.String"/>
<join/>
<element column="ADDRESS"/>
</field>
</class>
@Join
@Element(column="ADDRESS")
List<String> addresses;
}
The ACCOUNT table is as before, but this time we only have the "join table". In our MetaData we
used the <element> tag to specify the column name to use for the actual address String. In addition
we have an additional index column to form part of the primary key (along with the FK back to the
ACCOUNT table). You can override the default naming of this column by specifying the <order> tag.
64 Maps
.......................................................................................................................................
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses" persistence-modifier="persistent">
<map key-type="com.mydomain.Name" value-type="com.mydomain.Address"/>
<join/>
</field>
</class>
This will create 4 tables in the datastore, one for Account, one for Address, one for Name and a join
table containing foreign keys to the key/value tables.
If you want to configure the names of the columns in the "join" table you would use the <key> and
<value> subelements of <field>, something like this
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table attribute on the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the field element.
• To specify the name of the join table, specify the table attribute on the field element.
• To specify the names of the columns of the join table, specify the column attribute on the join,
key, and value elements.
• To specify the foreign-key between container table and join table, specify <foreign-key> below
the <join> element.
• To specify the foreign-key between join table and key table, specify <foreign-key> below the
<key> element.
• To specify the foreign-key between join table and value table, specify <foreign-key> below the
<value> element.
Which changes the names of the join table to ACCOUNT_ADDRESS from
ACCOUNT_ADDRESSES and the names of the columns in the join table from ACCOUNT_ID_OID
to ACCOUNT_ID, from NAME_ID_KID to NAME_ID, and from ADDRESS_ID_VID to
ADDRESS_ID.
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses" persistence-modifier="persistent">
<map key-type="java.lang.String" value-type="com.mydomain.Address"/>
<join/>
</field>
</class>
This will create 3 tables in the datastore, one for Account, one for Address and a join table also
containing the key.
If you want to configure the names of the columns in the "join" table you would use the <key> and
<value> subelements of <field> as shown above.
Please note that the column ADPT_PK_IDX is added by DataNucleus when the column type of the
key is not valid to be part of a primary key (with the RDBMS being used). If the column type of
your key is acceptable for use as part of a primary key then you will not have this "ADPT_PK_IDX"
column.
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses" persistence-modifier="persistent">
<map key-type="java.lang.String" value-type="java.lang.String"/>
<join/>
</field>
</class>
</package>
This results in just 2 tables. The "join" table contains both the key AND the value.
If you want to configure the names of the columns in the "join" table you would use the <key> and
<value> subelements of <field> as shown above.
Please note that the column ADPT_PK_IDX is added by DataNucleus when the column type of the
key is not valid to be part of a primary key (with the RDBMS being used). If the column type of
your key is acceptable for use as part of a primary key then you will not have this "ADPT_PK_IDX"
column.
64.2.5 Embedded
The above relationship types assume that all persistable classes in the 1-N relation will have their own
table. A variation on this is where you have a join table but you embed the keys, the values, or the
keys and the values of the map into this join table. This is described in Embedded Maps.
With these classes we want to store a foreign-key in the value table (ADDRESS), and we want to
use the "alias" field in the Address class as the key to the map. If you define the Meta-Data for these
classes as follows
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses" persistence-modifier="persistent" mapped-by="account">
<map key-type="java.lang.String" value-type="com.mydomain.Address"/>
<key mapped-by="alias"/>
</field>
</class>
This will create 2 tables in the datastore. One for Account, and one for Address. The table for Address
will contain the key field as well as an index to the Account record (notated by the mapped-by tag).
In this relationship, the Account class has a Map of Address objects, yet the Address knows nothing
about the Account. In this case we don't have a field in the Address to link back to the Account and so
DataNucleus has to use columns in the datastore representation of the Address class. So we define the
MetaData like this
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="addresses" persistence-modifier="persistent">
<map key-type="java.lang.String" value-type="com.mydomain.Address"/>
<key mapped-by="alias"/>
<value column="ACCOUNT_ID_OID"/>
</field>
</class>
Again there will be 2 tables, one for Address, and one for Account. Note that we have no "mapped-
by" attribute specified on the "field" element, and also no "join" element. If you wish to specify the
names of the columns used in the schema for the foreign key in the Address table you should use the
value element within the field of the map.
In terms of operation within your classes of assigning the objects in the relationship. You have to take
your Account object and add the Address to the Account map field since the Address knows nothing
about the Account. Also be aware that each Address object can have only one owner, since it has a
single foreign key to the Account. If you wish to have an Address assigned to multiple Accounts then
you should use the "Join Table" relationship above.
In this relationship, the Account class has a Map of Address objects, yet the Address knows
nothing about the Account. We don't have a field in the Address to link back to the Account and so
DataNucleus has to use columns in the datastore representation of the Address class. So we define the
MetaData like this
<package name="com.mydomain">
<class name="Account" identity-type="datastore">
...
<field name="phoneNumbers" persistence-modifier="persistent">
<map key-type="com.mydomain.Address" value-type="java.lang.String"/>
<key column="ACCOUNT_ID_OID"/>
<value mapped-by="businessPhoneNumber"/>
</field>
</class>
There will be 2 tables, one for Address, and one for Account. The key thing here is that we have
specified a "mapped-by" on the "value" element. Note that we have no "mapped-by" attribute
specified on the "field" element, and also no "join" element. If you wish to specify the names of the
columns used in the schema for the foreign key in the Address table you should use the key element
within the field of the map.
In terms of operation within your classes of assigning the objects in the relationship. You have to take
your Account object and add the Address to the Account map field since the Address knows nothing
about the Account. Also be aware that each Address object can have only one owner, since it has a
single foreign key to the Account. If you wish to have an Address assigned to multiple Accounts then
you should use the "Join Table" relationship above.
65 N-to-1 Relations
.......................................................................................................................................
so the Account class ("N" side) knows about the User class ("1" side), but not vice-versa and the
relation is stored using a join table. A particular user could be related to several accounts. If you
define the XML metadata for these classes as follows
<package name="mydomain">
<class name="User" identity-type="datastore">
...
</class>
@Join(table="ACCOUNT_USER")
User user;
}
For RDBMS this will create 3 tables in the database, one for User (with name USER), one for
Account (with name ACCOUNT), and a join table (with name ACCOUNT_USER) as shown below.
Note that in the case of non-RDBMS datastores there is no join-table, simply a "column" in the
ACCOUNT "table", storing the "id" of the related object
Things to note :-
• If you wish to specify the names of the database tables and columns for these classes, you can
use the attribute table (on the class element), the attribute name (on the column element) and the
attribute name (on the column attribute under join
65.1.3 Bidirectional
This relationship is described in the guide for 1-N relationships. In particular there are 2 ways to
define the relationship with RDBMS : the first uses a Join Table to hold the relationship, whilst the
second uses a Foreign Key in the "N" object to hold the relationship. For non-RDBMS datastores each
side will have a "column" (or equivalent) in the "table" of the N side storing the "id" of the related
(owning) object. Please refer to the 1-N relationships bidirectional relations since they show this exact
relationship.
66 M-to-N Relations
.......................................................................................................................................
Here the Product class knows about the Supplier class. In addition the Supplier knows about the
Product class, however with DataNucleus (as with the majority of JDO implementations) these
relationships are independent.
Please note that RDBMS supports the full range of options on this page, whereas other
datastores (ODF, Excel, HBase, MongoDB, etc) persist the Collection in a column in the owner
object and a column in the non-owner object rather than using join-tables since that concept is
RDBMS-only.
Please note when adding objects to an M-N relation, you MUST add to the owner side as a
minimum, and optionally also add to the non-owner side. Just adding to the non-owner side will
not add the relation.
The various possible relationships are described below.
• M-N Set relation
• M-N Ordered List relation
• M-N Indexed List - modelled as 2 1-N Unidirectional relations using Join Table
• M-N Map - modelled as 2 1-N Unidirectional using Join Table
<package name="mydomain">
<class name="Product" identity-type="datastore">
...
<field name="suppliers" table="PRODUCTS_SUPPLIERS">
<collection element-type="mydomain.Supplier"/>
<join>
<column name="PRODUCT_ID"/>
</join>
<element>
<column name="SUPPLIER_ID"/>
</element>
</field>
</class>
@Persistent(table="PRODUCTS_SUPPLIERS")
@Join(column="PRODUCT_ID")
@Element(column="SUPPLIER_ID")
Set<Supplier> suppliers;
}
@Persistent(mappedBy="suppliers")
Set<Products> products;
}
Note how we have specified the information only once regarding join table name, and join column
names as well as the <join>. This is the JDO standard way of specification, and results in a single join
table.
See also :-
<package name="mydomain">
<class name="Product" identity-type="datastore">
...
<field name="suppliers">
<collection element-type="mydomain.Supplier"/>
<order>
<extension vendor-name="datanucleus" key="list-ordering" value="id ASC"/>
</order>
<join/>
</field>
</class>
<field name="products">
<collection element-type="mydomain.Product"/>
<order>
<extension vendor-name="datanucleus" key="list-ordering" value="id ASC"/>
</order>
<join/>
</field>
</class>
</package>
or using annotations
@Persistent(table="PRODUCTS_SUPPLIERS")
@Join(column="PRODUCT_ID")
@Element(column="SUPPLIER_ID")
@Order(extensions=@Extension(vendorName="datanucleus", key="list-ordering", value="id ASC"))
List<Supplier> suppliers
}
@Persistent
@Order(extensions=@Extension(vendorName="datanucleus", key="list-ordering", value="id ASC"))
List<Product> products
}
There will be 3 tables, one for Product, one for Supplier, and the join table. The difference from the
Set example is that we now have <order-by> at both sides of the relation. This has no effect in the
datastore schema but when the Lists are retrieved they are ordered using the specified order-by.
Firstly a true M-N relation with Lists is impossible since there are two lists, and it is undefined
as to which one applies to which side etc. What is shown below is two independent 1-N
unidirectional join table relations. If you define the Meta-Data for these classes as follows
<package name="mydomain">
<class name="Product" identity-type="datastore">
...
<field name="suppliers" persistence-modifier="persistent">
<collection element-type="mydomain.Supplier"/>
<join/>
</field>
</class>
@Join
List<Supplier> suppliers;
}
@Join
List<Products> products;
}
There will be 4 tables, one for Product, one for Supplier, and the join tables. The difference from
the Set example is in the contents of the join tables. An index column is added to keep track of the
position of objects in the Lists.
In the case of a List at both ends it doesn't make sense to use a single join table because the ordering
can only be defined at one side, so you have to have 2 join tables.
<package name="mydomain">
<class name="Product" identity-type="datastore">
...
<field name="suppliers" persistence-modifier="persistent">
<map key-type="java.lang.String" value-type="mydomain.Supplier"/>
<join/>
</field>
</class>
This will create 4 tables in the datastore, one for Product, one for Supplier, and the join tables which
also contains the keys to the Maps (a String).
• To add an object to an M-N relationship you need to set it at both ends of the relation since the
relation is bidirectional and without such information the JDO implementation won't know which
end of the relation is correct.
• If you want to delete an object from one end of a M-N relationship you will have to remove it
first from the other objects relationship. If you don't you will get an error message that the object
to be deleted has links to other objects and so cannot be deleted.
67 Managing Relationships
.......................................................................................................................................
A a = new A();
B b = new B();
a.setB(b); // "a" knows about "b"
When the relation is bidirectional you have to set both sides of the relation. For example, we have
classes A and B and the class A has a collection of elements of type B, and B has a field of type A. So
we set it like this
A a = new A();
B b1 = new B();
a.addElement(b1); // "a" knows about "b1"
b1.setA(a); // "b1" knows about "a"
So it is really simple, with only 1 general rule. With a bidirectional relation you should set both
sides of the relation
For a 1-1 bidirectional relation, at persist you should set one side of the relation and the other
side will be set to make it consistent. If the respective sides are set to inconsistent objects then an
exception will be thrown at persist. At update of owner/non-owner side the other side will also be
updated to make them consistent.
For a 1-N bidirectional relation and you only specify the element owner then the collection must
be Set-based since DataNucleus cannot generate indexing information for you in that situation (you
must position the elements). At update of element or owner the other side will also be updated to
make them consistent. At delete of element the owner collection will also be updated to make them
consistent. If you are using a List you MUST set both sides of the relation
For an M-N bidirectional relation, at persist you MUST set the one side and the other side will be
populated at commit/flush to make them consistent.
This management of relations can be turned on/off using a PMF property
datanucleus.manageRelationships. If you always set both sides of a relation at persist or update then
you could safely turn it off.
When performing management of relations there are some checks implemented to spot typical errors in user
operations e.g add an element to a collection and then remove it (why?!). You can disable these checks using
datanucleus.manageRelationshipsChecks, set to false.
68 Cascading
.......................................................................................................................................
...
}
...
}
...
}
So we have an Owner of a collection of vintage Car's (1-N), and the Owner has a DrivingLicense
(1-1). We want to define lifecycle dependencies to match the relationships that we have between these
objects. Firstly lets look at the basic Meta-Data for the objects.
<class name="DrivingLicense">
<field name="serialNumber"/>
</class>
<class name="Car">
<field name="registrationNumber"/>
<field name="owner" persistence-modifier="persistent"/>
</class>
</package>
</jdo>
68.1.1 Persistence
JDO2 defines a concept called persistence-by-reachability. This means that when you persist an
object and it has a related persistable object then this other object is also persisted. So using our
example if we do
This results in both the Owner and the DrivingLicense objects being made persistent since the Owner
is passed to the PM operation and it has a field referring to the unpersisted DrivingLicense object. So
"reachability" will persist the license.
With DataNucleus you can actually turn off persistence-by-reachability for particular fields, by
specifying in the MetaData a DataNucleus extension tag, as follows
<class name="Owner">
<field name="license" persistence-modifier="persistent">
<extension vendor-name="datanucleus" key="cascade-persist" value="false"/>
</field>
...
</class>
So with this specification when we call makePersistent() with an object of type Owner then the field
"license" will not be persisted at that time.
68.1.2 Update
As mentioned above JDO2 defines a concept called persistence-by-reachability. This applies not
just to persist but also to update of objects, so when you update an object and its updated field has a
persistable object then that will be persisted. So using our example if we do
So when this field is updated the new DrivingLicense object will be made persistent since it is
reachable from the persistent Owner object.
With DataNucleus you can actually turn off update-by-reachability for particular fields, by specifying
in the MetaData a DataNucleus extension tag, as follows
<class name="Owner">
<field name="license" persistence-modifier="persistent">
<extension vendor-name="datanucleus" key="cascade-update" value="false"/>
</field>
...
</class>
So with this specification when we call makePersistent() to update an object of type Owner then the
field "license" will not be updated at that time.
<class name="DrivingLicense">
<field name="serialNumber"/>
</class>
<class name="Car">
<field name="registrationNumber"/>
<field name="owner" persistence-modifier="persistent" dependent="false"/>
</class>
</package>
</jdo>
So it was as simple as just adding dependent and dependent-element attributes to our related fields.
Notice that we also added one to the other end of the Owner-Car relationship, so that when a Car
comes to the end of its life, the Owner will not die with it. It may be the case that the owner dies
driving the car and they both die at the same time, but their deaths are independent!!
Just as we made use of dependent-element for collection fields, we also can make use of dependent-
key and dependent-value for map fields, and dependent-element for array fields.
Dependent Fields is utilised in the following situations
• An object is deleted (using deletePersistent()) and that object has relations to other objects. If the
other objects (either 1-1, 1-N, or M-N) are dependent then they are also deleted.
• An object has a 1-1 relation with another object, but the other object relation is nulled out. If the
other object is dependent then it is deleted when the relation is nulled.
• An object has a 1-N collection relation with other objects and the element is removed from the
collection. If the element is dependent then it will be deleted when removed from the collection.
The same happens when the collections is cleared.
• An object has a 1-N map relation with other objects and the key is removed from the map. If
the key or value are dependent and they are not present in the map more than once they will be
deleted when they are removed. The same happens when the map is cleared.
1. If dependent-field is true then use that to define the related objects to be deleted.
2. Else, if the column of the foreign-key field is NULLable then NULL it and leave the related
object alone
3. Else deleted the related object (and throw exceptions if this fails for whatever datastore-related
reason)
The other setting of datanucleus.deletionPolicy is "DataNucleus" which performs deletion of related
objects as follows
1. If dependent-field is true then use that to define the related objects to be deleted.
2. If a foreign-key is specified (in MetaData) for the relation field then leave any deletion to the
datastore to perform (or throw exceptions as necessary)
3. Else, if the column of the foreign-key field is NULLable then NULL it and leave the related
object alone
4. Else deleted the related object (and throw exceptions if this fails for whatever datastore-related
reason)
So, as you can see, with the second option you have the ability to utilise datastore "referential
integrity" checking using your MetaData-specified <foreign-key> elements.
// "bob" and "license2" will be persisted but "license" wont be since not persisted explicitly
// and at commit it is no longer reachable from a persisted object
tx.commit();
With DataNucleus you can turn off persistence-by-reachability at commit by setting the
PersistenceManagerFactory property datanucleus.persistenceByReachabilityAtCommit to false.
69 MetaData Reference
.......................................................................................................................................
• XML : the traditional mechanism, with XML files containing information for each class to be
persisted. As a further complication you can define basic persistence metadata for a class in one
file, and then ORM metadata for that class in a separate file (since the ORM metadata is specific
to a certain datastore).
• Annotations : using JDK1.5+ annotations in the classes to be persisted
• API : a programmatic API allowing definition of which classes are to be persisted at runtime
When not using the MetaData API we recommend that you use either XML or annotations for the basic
persistence information, but always use XML for ORM information. This is because it is liable to change at
deployment time and hence is accessible when in XML form whereas in annotations you add an extra compile
cycle (and also you may need to deploy to some other datastore at some point, hence needing a different
deployment).
• API Metadata
• ORM XML Metadata
• JDO XML Metadata
• Annotations
So if a class has Metadata defined by API then that will override all other Metadata. If a class
has annotations and JDO XML Metadata then the XML Metadata will take precedence over the
annotations (or rather be merged on top of the annotations). You can use whichever (or multiple) of
the above forms at the same time for a single class.
META-INF/package.jdo
WEB-INF/package.jdo
package.jdo
com/package.jdo
com/mycompany/package.jdo
com/mycompany/sample/package.jdo
com/mycompany/sample/myexample.jdo
In addition, for this example, DataNucleus allows the previous JDO 1.0.0 alternatives of
com.jdo
com/mycompany.jdo
com/mycompany/sample.jdo
In addition to the above, you can split your MetaData definitions between JDO MetaData files. For
example if you have the following classes
com/mycompany/A.java
com/mycompany/B.java
com/mycompany/C.java
com/mycompany/app1/D.java
com/mycompany/app1/E.java
You could define the MetaData for these 5 classes in many ways -- for example put all class
definitions in com/mycompany/package.jdo, or put the definitions for D and E in com/mycompany/
app1/package.jdo and the definitions for A, B, C in com/mycompany/package.jdo, or have some
in their class named MetaData files e.g com/mycompany/app1/A.jdo, or a mixture of the above.
DataNucleus will always search for the MetaData file containing the class definition for the class that
it requires.
By default any XML Metadata will be validated for accuracy when loading it. Obviously XML is
defined by a DTD or XSD schema and so should follow that. You can turn off such validations by
setting the persistence property datanucleus.metadata.xml.validate to false when creating your
PMF. Note that this only turns off the XML strictness validation, and not the checks on inconsistency
of specification of relations etc.
You can use ORM metadata to override particular datastore-specific things like table and column
names. If your application doesn't make use of ORM metadata then you could turn off the searches
for ORM Metadata files when a class is loaded up. You do this with the persistence property
datanucleus.metadata.supportORM setting it to false.
JDO provides a mechanism whereby when a class is initialised (by the ClassLoader) any
PersistenceManagerFactory is notified of its existence, and its Metadata can be loaded. This is
enabled by the enhancement process. If you decided that you maybe only wanted some classes present
in one PMF and other classes present in a different PMF then you can disable this and leave it to
DataNucleus to discover the Metadata when operations are performed on that PMF. The persistence
property to define to disable this is datanucleus.metadata.autoregistration (setting it to false).
70 XML
.......................................................................................................................................
Here is an example header for package.orm files with ORM XSD specification
What follows provides a reference guide to MetaData elements (refer to the relevant XSD for precise
details).
• jdo
• package
• class
• datastore-identity
• column
• extension
• primary-key
• column
• inheritance
• discriminator
• column
• join
• column
• version
• column
• extension
• join
• column
• foreign-key
• column
• field
• property
• index
• column
• field
• property
• unique
• column
• field
• property
• field
• collection
• extension
• map
• extension
• array
• join
• primary-key
• index
• column
• embedded
• field
• column
• element
• column
• key
• column
• value
• column
• order
• column
• extension
• column
• extension
• foreign-key
• column
• index
• column
• unique
• column
• extension
• property
• collection
• extension
• map
• extension
• array
• join
• primary-key
• index
• column
• embedded
• field
• column
• element
• column
• key
• column
• value
• column
• order
• column
• column
• extension
• foreign-key
• column
• index
• column
• unique
• column
• extension
• fetch-group
• field
• query
• sequence
• extension
• fetch-plan
• extension
• extension
definition of datastore identity to be used. This element can contain column sub-elements allowing
definition of the column details where required - these are optional.
These are attributes within the <extension> tag (jdo/package/class/field/join/extension). These are for
controlling the join table.
These are attributes within the <extension> tag (jdo/package/class/sequence/extension). These are for
controlling the datastore sequences created by DataNucleus. Please refer to the documentation for the
value generator being used for applicability
71 Annotations
.......................................................................................................................................
71.1.1 @PersistenceCapable
This annotation is used when you want to mark a class as persistent. It equates to the <class> XML
element (though with only some of its attributes). Specified on the class.
@PersistenceCapable(identityType=IdentityType.APPLICATION)
public class MyClass
{
...
}
71.1.2 @PersistenceAware
This annotation is used when you want to mark a class as being used in persistence but not being
persistable. That is "persistence-aware" in JDO2 terminology. It has no attributes. Specified on the
class.
@PersistenceAware
public class MyClass
{
...
}
71.1.3 @Cacheable
This annotation is a shortcut for @PersistenceCapable(cacheable={value}) specifying whether the
class can be cached in a Level 2 cache. Specified on the class. The default
@Cacheable("false")
public class MyClass
{
...
}
71.1.4 @EmbeddedOnly
This annotation is a shortcut for @PersistenceCapable(embeddedOnly="true") meaning that the class
can only be persisted embedded into another class. It has no attributes. Specified on the class.
@EmbeddedOnly
public class MyClass
{
...
}
71.1.5 @Inheritance
Annotation used to define the inheritance for a class. Specified on the class.
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
public class MyClass
{
...
}
71.1.6 @Discriminator
Annotation used to define a discriminator to be stored with instances of this class and is used to
determine the types of the objects being stored. Specified on the class.
@PersistenceCapable
@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
@Discriminator(strategy=DiscriminatorStrategy.CLASS_NAME)
public class MyClass
{
...
}
71.1.7 @DatastoreIdentity
Annotation used to define the identity when using datastore-identity for the class. Specified on the
class.
@PersistenceCapable
@DatastoreIdentity(strategy=IdGeneratorStrategy.INCREMENT)
public class MyClass
{
...
}
71.1.8 @Version
Annotation used to define the versioning details for use with optimistic transactions. Specified on the
class.
@PersistenceCapable
@Version(strategy=VersionStrategy.VERSION_NUMBER)
public class MyClass
{
...
}
71.1.9 @PrimaryKey
Annotation used to define the primary key constraint for a class. Maps across to the <primary-key>
XML element. Specified on the class.
@PersistenceCapable
@PrimaryKey(name="MYCLASS_PK")
public class MyClass
{
...
}
71.1.10 @FetchPlans
Annotation used to define a set of fetch plans. Specified on the class. Used by named queries
@PersistenceCapable
@FetchPlans({@FetchPlan(name="plan_3", maxFetchDepth=3, fetchGroups={"group1", "group4"}),
@FetchPlan(name="plan_4", maxFetchDepth=2, fetchGroups={"group1", "group2"})})
public class MyClass
{
...
}
71.1.11 @FetchPlan
Annotation used to define a fetch plan Is equivalent to the <fetch-plan> XML element. Specified on
the class. Used by named queries
@PersistenceCapable
@FetchPlan(name="plan_3", maxFetchDepth=3, fetchGroups={"group1", "group4"})
public class MyClass
{
...
}
71.1.12 @FetchGroups
Annotation used to define a set of fetch groups for a class. Specified on the class.
@PersistenceCapable
@FetchGroups({@FetchGroup(name="one_two", members={@Persistent(name="field1"), @Persistent(name="field2")
@FetchGroup(name="three", members={@Persistent(name="field3")})})
public class MyClass
{
@Persistent
String field1;
@Persistent
String field2;
@Persistent
String field3;
...
}
71.1.13 @FetchGroup
Annotation used to define a fetch group. Is equivalent to the <fetch-group> XML element. Specified
on the class.
@PersistenceCapable
@FetchGroup(name="one_two", members={@Persistent(name="field1"), @Persistent(name="field2")})
public class MyClass
{
@Persistent
String field1;
@Persistent
String field2;
...
}
71.1.14 @Sequence
Annotation used to define a sequence generator. Is equivalent to the <sequence> XML element.
Specified on the class.
71.1.15 @Queries
Annotation used to define a set of named queries for a class. Specified on the class.
@PersistenceCapable
@Queries({@Query(name="PeopleCalledSmith", language="JDOQL",
value="SELECT FROM org.datanucleus.samples.Person WHERE surname == \"Smith\""),
@Query(name="PeopleCalledJones", language="JDOQL",
value="SELECT FROM org.datanucleus.samples.Person WHERE surname == \"Jones\"")})
public class Person
{
@Persistent
String surname;
...
}
71.1.16 @Query
Annotation used to define a named query. Is equivalent to the <query> XML element. Specified on
the class.
@PersistenceCapable
@Query(name="PeopleCalledSmith", language="JDOQL",
value="SELECT FROM org.datanucleus.samples.Person WHERE surname == \"Smith\"")
public class Person
{
@Persistent
String surname;
...
}
71.1.17 @Indices
Annotation used to define a set of indices for a class. Specified on the class.
@PersistenceCapable
@Indices({@Index(name="MYINDEX_1", members={"field1","field2"}), @Index(name="MYINDEX_2", members={"field
public class Person
{
...
}
71.1.18 @Index
Annotation used to define an index for the class as a whole typically being a composite index across
multiple columns or fields/properties. Is equivalent to the <index> XML element when specified
under class. Specified on the class.
@PersistenceCapable
@Index(name="MY_COMPOSITE_IDX", members={"field1", "field2"})
public class MyClass
{
@Persistent
String field1;
@Persistent
String field2;
...
}
71.1.19 @Uniques
Annotation used to define a set of unique constraints for a class. Specified on the class.
@PersistenceCapable
@Uniques({@Unique(name="MYCONST_1", members={"field1","field2"}), @Unique(name="MYCONST_2", members={"fie
public class Person
{
...
}
71.1.20 @Unique
Annotation used to define a unique constraints for the class as a whole typically being a composite
constraint across multiple columns or fields/properties. Is equivalent to the <unique> XML element
when specified under class. Specified on the class.
@PersistenceCapable
@Unique(name="MY_COMPOSITE_IDX", members={"field1", "field2"})
public class MyClass
{
@Persistent
String field1;
@Persistent
String field2;
...
}
71.1.21 @ForeignKeys
Annotation used to define a set of foreign-key constraints for a class. Specified on the class.
71.1.22 @ForeignKey
Annotation used to define a foreign-key constraint for the class. Specified on the class.
71.1.23 @Joins
Annotation used to define a set of joins (to secondary tables) for a class. Specified on the class.
@PersistenceCapable
@Joins({@Join(table="MY_OTHER_TABLE", column="MY_PK_COL"),
@Join(table="MY_SECOND_TABLE", column="MY_PK_COL")})
public class MyClass
{
@Persistent(table="MY_OTHER_TABLE")
String myField;
@Persistent(table="MY_SECOND_TABLE")
String myField2;
...
}
71.1.24 @Join
Annotation used to specify a join for a secondary table. Specified on the class.
@PersistenceCapable(name="MYTABLE")
@Join(table="MY_OTHER_TABLE", column="MY_PK_COL")
public class MyClass
{
@Persistent(name="MY_OTHER_TABLE")
String myField;
...
}
71.1.25 @Columns
Annotation used to define the columns which have no associated field in the class. User should
specify a minimum of @Column "name", "jdbcType", and "insertValue". Specified on the class.
@PersistenceCapable
@Columns(@Column(name="MY_OTHER_COL", jdbcType="VARCHAR", insertValue="N/A")
public class MyClass
{
...
}
71.1.26 @Persistent
Annotation used to define the fields/properties to be persisted. Is equivalent to the <field> and
<property> XML elements. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Persistent(primaryKey="true")
String myField;
...
}
71.1.27 @Serialized
This annotation is a shortcut for @Persistent(serialized="true") meaning that the field is stored
serialized. It has no attributes. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Serialized
Object myField;
...
}
71.1.28 @NotPersistent
This annotation is a shortcut for @Persistent(persistenceModifier=PersistenceModifier.NONE)
meaning that the field/property is not persisted. It has no attributes. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@NotPersistent
String myOtherField;
...
}
71.1.29 @Transactional
This annotation is a shortcut for
@Persistent(persistenceModifier=PersistenceModifier.TRANSACTIONAL) meaning that the field/
property is not persisted yet managed. It has no attributes. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Transactional
String myOtherField;
...
}
71.1.30 @Cacheable
This annotation is a shortcut for @Persistent(cacheable={value}) specifying whether the field/
property can be cached in a Level 2 cache. Specified on the field/property. The default
71.1.31 @PrimaryKey
This annotation is a shortcut for @Persistent(primaryKey="true") meaning that the field/property is
part of the primary key for the class. No attributes are needed when specified like this. Specified on
the field/method.
@PersistenceCapable
public class MyClass
{
@PrimaryKey
String myOtherField;
...
}
71.1.32 @Element
Annotation used to define the element for any collection/array to be persisted. Maps across to the
<collection>, <array> and <element> XML elements. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Element(types=org.datanucleus.samples.MyElementClass.class, dependent="true")
Collection myField;
...
}
71.1.33 @Order
Annotation used to define the ordering of an order-based Collection/array to be persisted. Maps across
to the <order> XML element. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Element(types=org.datanucleus.samples.MyElementClass.class, dependent="true")
@Order(column="ORDER_IDX")
Collection myField;
...
}
71.1.34 @Key
Annotation used to define the key for any map to be persisted. Maps across to the <map> and <key>
XML elements. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Key(types=java.lang.String.class)
Map myField;
...
}
71.1.35 @Value
Annotation used to define the value for any map to be persisted. Maps across to the <map> and
<value> XML elements. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Key(types=java.lang.String.class)
@Value(types=org.datanucleus.samples.MyValueClass.class, dependent="true")
Map myField;
...
}
71.1.36 @Join
Annotation used to specify a join to a join table for a collection/array/map. Specified on the field/
method.
@PersistenceCapable
public class MyClass
{
@Persistent
@Element(types=org.datanucleus.samples.MyElement.class)
@Join(table="MYCLASS_ELEMENTS", column="MYCLASS_ELEMENTS_PK")
Collection myField;
...
}
71.1.37 @Embedded
Annotation used to define that the field contents is embedded into the same table as this field Maps
across to the <embedded> XML element. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Embedded(members={
@Persistent(name="field1", columns=@Column(name="OTHER_FLD_1")),
@Persistent(name="field2", columns=@Column(name="OTHER_FLD_2"))
}
MyOtherClass myField;
...
}
@PersistenceCapable
@EmbeddedOnly
public class MyOtherClass
{
@Persistent
String field1;
@Persistent
String field2;
}
71.1.38 @Columns
Annotation used to define the columns into which a field is persisted. If the field is persisted into a
single column then @Column should be used. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Persistent
@Columns({@Column(name="RED"), @Column(name="GREEN"), @Column(name="BLUE"), @Column(name="ALPHA")})
Color myField;
...
}
71.1.39 @Column
Annotation used to define that the colum where a field is persisted. Is equivalent to the <column>
XML element when specified under field. Specified on the field/method (and within other
annotations).
@PersistenceCapable
public class MyClass
{
@Persistent
@Column(name="MYCOL", jdbcType="VARCHAR", length=40)
String field1;
...
}
71.1.40 @Index
Annotation used to define that this field is indexed. Is equivalent to the <index> XML element when
specified under field. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Persistent
@Index(name="MYFIELD1_IDX")
String field1;
@Persistent
@Index(name="MYFIELD2_IDX", unique="true")
String field2;
...
}
71.1.41 @Unique
Annotation used to define that this field has a unique constraint. Is equivalent to the <unique> XML
element when specified under field. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Persistent
@Unique(name="MYFIELD1_IDX")
String field1;
...
}
71.1.42 @ForeignKey
Annotation used to define the foreign key for a relationship field. Is equivalent to the <foreign-key>
XML element when specified under field. Specified on the field/method.
@PersistenceCapable
public class MyClass
{
@Persistent
@ForeignKey(name="MYFIELD1_FK", deleteAction=ForeignKeyAction.RESTRICT)
String field1;
...
}
71.1.43 @Extensions
Annotation used to define a set of extensions specific to the JDO2 implementation being used.
Specified on the class or field.
@PersistenceCapable
@Extensions({@Extension(vendorName="datanucleus", key="firstExtension", value="myValue"),
@Extension(vendorName="datanucleus", key="secondExtension", value="myValue")})
public class Person
{
...
}
71.1.44 @Extension
Annotation used to define an extension specific to a particular JDO implementation. Is equivalent to
the <extension> XML element. Specified on the class or field.
@PersistenceCapable
@Extension(vendorName="DataNucleus", key="RunFast", value="true")
public class Person
{
...
}
72 MetaData API
.......................................................................................................................................
So we have a JDOMetadata object and want to define the persistence for our class
mydomain.MyClass, so we do as follows
So we follow the same structure of the JDO XML Metadata file adding packages to the top level,
and classes to the respective package. Note that we could have achieved this by a simple typesafe
invocation
So now we have the class defined, we need to set its key information
cmd.setTable("CLIENT").setDetachable(true).setIdentityType(IdentityType.DATASTORE);
cmd.setPersistenceModifier(ClassPersistenceModifier.PERSISTENCE_CAPABLE);
And we define also define fields/properties via the API in a similar way
Note that, just like with XML metadata, we don't need to add information for all fields since they have
their own default persistence settings based on the type of the field.
All that remains is to register the metadata with the persistence process
pmf.registerMetadata(md);
and we can now inspect the information, casting the compmd to either
javax.jdo.metadata.ClassMetadata or javax.jdo.metadata.InterfaceMetadata.
Please note that you cannot currently change metadata retrieved in this way, only view it
73 ORM MetaData
.......................................................................................................................................
package mydomain;
...
}
and I want to use an existing schema. With this case I need to define the table and column names
that it maps to. To do this I need to use JDO 2 ORM tags. So I come up with MetaData as follows in
package.jdo
So you see that our class is being mapped across to a table "PERSON" in the datastore, with columns
"TITLE", "FORENAME", "SURNAME". We have also specified that the upper size limit on the
forename and surname fields is 100.
74 Schema Mapping
.......................................................................................................................................
In our case we want to map this class to a table called ESTABLISHMENT, and has columns NAME,
DIRECTION, PHONE and NUMBER_OF_ROOMS (amongst other things). So we define our Meta-
Data like this
So we have defined the table and the column names. It should be mentioned that if you don't specify
the table and column names then DataNucleus will generate names for the datastore identifiers. The
table name will be based on the class name, and the column names will be based on the field names
and the role of the field (if part of a relationship).
See also :-
• Identifier Guide - defining the identifiers to use for table/column names
• MetaData reference for <column> element
• MetaData reference for <primary-key> element
• Annotations reference for @Column
• Annotations reference for @PrimaryKey
We want to define the names of the identity column in "MYCLASS" and "MYSUBCLASS". Here's
how we do it
See also :-
Defining the column name for "MyClass.id" is easy since we use the same as shown previously
"column" for the field. Obviously the table "MYSUBCLASS" will also need a PK column. Here's
how we define the column mapping
See also :-
• Inheritance Guide - defining how to use inheritance between classes
• MetaData reference for <inheritance> element
In this class we can model payments from a customer of an amount. Where the customer pays by bank
transfer we can save the reference number. Since our hotel is in the United Kingdom we want the
default currency to be pounds, or to use its ISO4217 currency code "GBP". In addition, since the bank
transfer reference is optional we want that column to be nullable. So let's specify the MetaData for the
class.
<class name="Payment">
<field name="customer" persistence-capable="persistent" column="CUSTOMER_ID"/>
<field name="bankTransferReference">
<column name="TRANSFER_REF" allows-null="true"/>
</field>
<field name="currency">
<column name="CURRENCY" default-value="GBP"/>
</field>
<field name="amount" column="AMOUNT"/>
</class>
So we make use of the allows-null and default-value attributes. The table, when created by
DataNucleus, will then provide the default and nullability that we require.
See also :-
<class name="Payment">
<field name="customer" persistence-capable="persistent" column="CUSTOMER_ID">
<field name="bankTransferReference">
<column name="TRANSFER_REF" jdbc-type="VARCHAR" length="255" allows-null="true"/>
</field>
<field name="currency">
<column name="CURRENCY" jdbc-type="CHAR" length="3" default-value="GBP"/>
</field>
<field name="amount">
<column name="AMOUNT" jdbc-type="DECIMAL" length="10" scale="2"/>
</field>
</class>
So in this example our table "ESTABLISHMENT" has the columns associated with the specified
fields and also has columns "YEAR_ESTABLISHED" (that is INTEGER-based and will be given a
value of "1980" on any inserts) and "MANAGER_NAME" (VARCHAR-based and will be given a
value of "N/A" on any inserts).
74.1.7 columnposition
With some datastores it is desirable to be able to specify the relative position of a column in the table
schema. The default (for DataNucleus) is just to put them in ascending alphabetical order. JDO allows
definition of this using the position attribute on a column. See fields/properties column positioning
docs for details.
75 Multitenancy
.......................................................................................................................................
<class name="MyClass">
<extension vendor-name="datanucleus" key="multitenancy-column-name" value="TENANT"/>
<extension vendor-name="datanucleus" key="multitenancy-column-length" value="24"/>
...
</class>
In all subsequent use of DataNucleus, any "insert" to the primary "table"(s) will also include the
TENANT column value. Additionally any query will apply a WHERE clause restricting to a
particular value of TENANT column.
If you want to disable multitenancy on a class, just specify the following metadata
<class name="MyClass">
<extension vendor-name="datanucleus" key="multitenancy-disable" value="true"/>
...
</class>
76 Datastore Identifiers
.......................................................................................................................................
class MyClass
{
String myField1;
Collection<MyElement> elements1; // Using join table
Collection<MyElement> elements2; // Using foreign-key
}
class MyElement
{
String myElementField;
MyClass myClass2;
}
Using the example above, the rules in this NamingFactory mean that, assuming that the user doesn't
specify any <column> elements :-
The NamingFactory "jpa" aims at providing a naming policy consistent with the "JPA" specification.
Using the same example above, the rules in this NamingFactory mean that, assuming that the user
doesn't specify any <column> elements :-
This became the default for JDO persistence from DataNucleus v2.x onwards and changes a few
things over the previous "datanucleus1" factory, attempting to make the naming more concise and
consistent (we retain "datanucleus1" for backwards compatibility).
Using the same example above, the rules in this RDBMS IdentifierFactory mean that, assuming that
the user doesnt specify any <column> elements :-
• MyClass will be persisted into a table named MYCLASS
• When using datastore identity MYCLASS will have a column called MYCLASS_ID
• MyClass.myField1 will be persisted into a column called MYFIELD1
• MyElement will be persisted into a table named MYELEMENT
• MyClass.elements1 will be persisted into a join table called MYCLASS_ELEMENTS1
• MYCLASS_ELEMENTS1 will have columns called MYCLASS_ID_OID (FK to owner table)
and MYELEMENT_ID_EID (FK to element table)
• MYCLASS_ELEMENTS1 will have column names like STRING_ELE, STRING_KEY,
STRING_VAL for non-PC elements/keys/values of collections/maps
• MyClass.elements2 will be persisted into a column ELEMENTS2_MYCLASS_ID_OWN or
ELEMENTS2_MYCLASS_ID_OID (FK to owner) table
• Any discriminator column will be called DISCRIMINATOR
• Any index column in a List will be called IDX
• Any adapter column added to a join table to form part of the primary key will be called IDX
• Any version column for a table will be called VERSION
This was the default in DataNucleus v1.x for JDO persistence and provided a reasonable default
naming of datastore identifiers using the class and field names as its basis.
Using the example above, the rules in this RDBMS IdentifierFactory mean that, assuming that the user
doesnt specify any <column> elements :-
• MyClass will be persisted into a table named MYCLASS
• When using datastore identity MYCLASS will have a column called MYCLASS_ID
• MyClass.myField1 will be persisted into a column called MY_FIELD1
• MyElement will be persisted into a table named MYELEMENT
• MyClass.elements1 will be persisted into a join table called MYCLASS_ELEMENTS1
• MYCLASS_ELEMENTS1 will have columns called MYCLASS_ID_OID (FK to owner table)
and MYELEMENT_ID_EID (FK to element table)
• MYCLASS_ELEMENTS1 will have column names like STRING_ELE, STRING_KEY,
STRING_VAL for non-PC elements/keys/values of collections/maps
• MyClass.elements2 will be persisted into a column ELEMENTS2_MYCLASS_ID_OID or
ELEMENTS2_ID_OID (FK to owner) table
• Any discriminator column will be called DISCRIMINATOR
• Any index column in a List will be called INTEGER_IDX
• Any adapter column added to a join table to form part of the primary key will be called
ADPT_PK_IDX
• Any version column for a table will be called OPT_VERSION
The RDBMS IdentifierFactory "jpa" aims at providing a naming policy consistent with the "JPA"
specification.
Using the same example above, the rules in this RDBMS IdentifierFactory mean that, assuming that
the user doesnt specify any <column> elements :-
• MyClass will be persisted into a table named MYCLASS
• When using datastore identity MYCLASS will have a column called MYCLASS_ID
• MyClass.myField1 will be persisted into a column called MYFIELD1
• MyElement will be persisted into a table named MYELEMENT
• MyClass.elements1 will be persisted into a join table called MYCLASS_MYELEMENT
• MYCLASS_ELEMENTS1 will have columns called MYCLASS_MYCLASS_ID (FK to
owner table) and ELEMENTS1_ELEMENT_ID (FK to element table)
• MyClass.elements2 will be persisted into a column ELEMENTS2_MYCLASS_ID (FK to
owner) table
• Any discriminator column will be called DTYPE
• Any index column in a List for field MyClass.myField1 will be called MYFIELD1_ORDER
• Any adapter column added to a join table to form part of the primary key will be called IDX
• Any version column for a table will be called VERSION
This RDBMS IdentifierFactory exists for backward compatibility with JPOX 1.2.0. If you experience
changes of schema identifiers when migrating from JPOX 1.2.0 to datanucleus, you should give this
one a try.
Schema compatibility between JPOX 1.2.0 and datanucleus had been broken e.g. by the number of
characters used in hash codes when truncating identifiers: this has changed from 2 to 4.
your own identifiers for things like schema/catalog etc then you need to specify those using the
case you wish to use in the datastore (including quoting as necessary)
77 Secondary Tables
.......................................................................................................................................
Applicable to RDBMS
The standard JDO persistence strategy is to persist an object of a class into its own table. In some
situations you may wish to map the class to a primary table as well as one or more secondary tables.
For example when you have a Java class that could have been split up into 2 separate classes yet, for
whatever reason, has been written as a single class, however you have a legacy datastore and you need
to map objects of this class into 2 tables. JDO allows persistence of fields of a class into secondary
tables.
The process for managing this situation is best demonstrated with an example. Let's suppose we have
a class that represents a Printer. The Printer class contains within it various attributes of the toner
cartridge. So we have
package com.mydomain.samples.secondarytable;
String tonerModel;
int tonerLifetime;
/**
* Constructor.
* @param make Make of printer (e.g Hewlett-Packard)
* @param model Model of Printer (e.g LaserJet 1200L)
* @param tonerModel Model of toner cartridge
* @param tonerLifetime lifetime of toner (number of prints)
*/
public Printer(String make, String model, String tonerModel, int tonerLifetime)
{
this.make = make;
this.model = model;
this.tonerModel = tonerModel;
this.tonerLifetime = tonerLifetime;
}
Now we have a database schema that has 2 tables (PRINTER and PRINTER_TONER) in which to
store objects of this class. So we need to tell DataNucleus to perform this mapping. So we define the
MetaData for the Printer class like this
So here we have defined that objects of the Printer class will be stored in the primary table
PRINTER. In addition we have defined that some fields are stored in the table PRINTER_TONER.
This is achieved by way of
• We will store tonerModel and tonerLifetime in the table PRINTER_TONER. This is achieved by
using <field table="PRINTER_TONER">
• The table PRINTER_TONER will use a primary key column called PRINTER_REFID. This is
achieved by using <join table="PRINTER_TONER" column="PRINTER_REFID"/>
You could equally specify this using annotations
@PersistenceCapable
@Join(table="PRINTER_TONER", column="PRINTER_REFID")
public class Printer
{
@Persistent(primaryKey="true", column="PRINTER_ID")
long id;
@Column(name="MAKE")
String make;
@Column(name="MODEL")
String model;
@Persistent(table="PRINTER_TONER", column="MODEL")
String tonerModel;
@Persistent(table="PRINTER_TONER", column="LIFETIME")
int tonerLifetime;
...
}
So we now have our primary and secondary database tables. The primary key of the
PRINTER_TONER table serves as a foreign key to the primary class. Whenever we persist a Printer
object a row will be inserted into both of these tables.
So this will create the primary key constraint with the name "TONER_PK".
See also :-
• MetaData reference for <primary-key> element
• MetaData reference for <join> element
• Annotations reference for @PrimaryKey
• Annotations reference for @Join
• Secondary Table tutorial
...
}
We now need to specify which fields we want to store in any secondary tables. To do this we can
define the metadata like this
<?xml version="1.0"?>
<!DOCTYPE jdo PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/jdo_2_0.dtd">
<jdo>
<package name="mydomain">
<class name="Printer" table="PRINTER">
<datastore-identity>
<column name="ID"/>
</datastore-identity>
<join table="TONER">
<column name="ID"/>
</join>
<field name="make"/>
<field name="model"/>
<field name="tonerModel" table="TONER"/>
<field name="tonerLifetime" table="TONER"/>
</class>
</package>
</jdo>
With this we have stated that the fields make and model will be stored in the default table, that we
named PRINTER, and that tonerModel and tonerLifetime we be stored in the table TONER. The
tables will both store the unique identity assigned to the objects we persist, in this case we have
specified the column name ID for both tables, though we would usually only do this when working to
an existing schema. When we retrieve any of our stored objects the tables will be joined automatically
by matching the identities.
We can see how this works in more detail by setting the query logging to DEBUG (set
log4j.category.DataNucleus.Query=DEBUG, in your log4j.properties file). We can retrieve all of
our stored Printer objects by performing the following query
Query q = pm.newQuery(Printer.class);
List<Printer> list = (List<Printer>)q.execute();
Now if we look in our log file we can see how this has been converted into the appropriate query
language for our datastore. With an RDBMS datastore using SQL, for example, we get
SELECT FROM mydomain.Printer Query compiled to datastore query
"SELECT 'mydomain.Printer' AS NUCLEUS_TYPE,`A0`.`MAKE`,`A0`.`MODEL`,`A1`.`TONER_MODEL`,
`A1`.`TONER_LIFETIME`,`A0`.`ID`
FROM `PRINTER` `A0` INNER JOIN `TONER` `A1` ON `A0`.`ID` = `A1`.`ID`"
So we can see that in this case an INNER JOIN was performed using the ID columns as expected.
This worked example was provided by a DataNucleus user Tom Robson
78 Constraints
.......................................................................................................................................
• Indexes - these are used to mark fields that are referenced often as indexes so that when they are
used the performance is optimised.
• Unique constraints - these are placed on fields that should have a unique value. That is only one
object will have a particular value.
• Foreign-Keys - these are used to interrelate objects, and allow the datastore to keep the integrity
of the data in the datastore.
• Primary-Keys - allow the PK to be set, and also to have a name.
78.1.1 Indexes
Many datastores provide the ability to have indexes defined to give performance benefits. With
RDBMS the indexes are specified on the table and the indexes to the rows are stored separately. In the
same way an ODBMS typically allows indexes to be specified on the fields of the class, and these are
managed by the datastore. JDO provides a mechanism for defining indexes, and hence if a developer
knows that a particular field is going to be highly used for querying, they can select that field to be
indexed in their (JDO) persistence solution. Let's take an example class, and show how to specify this
We decide that our bookingType is going to be highly used and we want to index this in the
persistence tool. To do this we define the Meta-Data for our class as
<class name="Booking">
<field name="bookingType">
<index name="BOOKING_TYPE_INDEX"/>
</field>
</class>
This will mean that DataNucleus will create an index in the datastore for the field and the index will
have the name BOOKING_TYPE_INDEX (for datastores that support using named indexes). If we had
wanted the index to provide uniqueness, we could have made this
This has demonstrated indexing the fields of a class. The above example will index together all
columns for that field. In certain circumstances you want to be able to index from the column point of
view. So we are thinking more from a database perspective. Here we define our indexes at the <class>
level, like this
<class name="Booking">
<index name="MY_BOOKING_INDEX">
<column name="BOOKING"/>
</index>
...
</class>
This creates an index for the specified column (where the datastore supports columns i.e RDBMS).
Should you have need to tailor the index creation, for example to generate a particular type of index
(where the datastore supports it) , you can specify extended settings that is appended to the end of any
CREATE INDEX statement.
<class name="Booking">
<index name="MY_BOOKING_INDEX">
<extension vendor-name="datanucleus" key="extended-setting" value=" USING HASH"/>
</index>
...
</class>
See also :-
• MetaData reference for <index> element
• Annotations reference for @Index
• Annotations reference for @Index (class level)
Some datastores provide the ability to have unique constraints defined on tables to give extra control
over data integrity. JDO provides a mechanism for defining such unique constraints. Lets take the
previous class, and show how to specify this
<class name="Booking">
<field name="bookingType">
<unique name="BOOKING_TYPE_CONSTRAINT"/>
</field>
</class>
So in an identical way to the specification of an index. This example specification will result in the
column(s) for "bookingType" being enforced as unique in the datastore. In the same way you can
specify unique constraints directly to columns - see the example above for indexes.
Again, as for index, you can also specify unique constraints at "class" level in the MetaData file.
This is useful to specify where the composite of 2 or more columns or fields are unique. So with this
example
<class name="Booking">
<unique name="UNIQUE_PERF">
<field name="performanceDate"/>
<field name="startTime"/>
</unique>
<field name="performanceDate"/>
<field name="startTime"/>
</class>
The table for Booking has a unique constraint on the columns for the fields performanceDate and
startTime
See also :-
Applicable to RDBMS
When objects have relationships with one object containing, for example, a Collection of another
object, it is common to store a foreign key in the datastore representation to link the two associated
tables. Moreover, it is common to define behaviour about what happens to the dependent object when
the owning object is deleted. Should the deletion of the owner cause the deletion of the dependent
object maybe ? Lets take an example
We now want to control the relationship so that it is linked by a named foreign key, and that we
cascade delete the Room object when we delete the Hotel. We define the Meta-Data like this
<class name="Hotel">
<field name="rooms">
<collection element-type="com.mydomain.samples.hotel.Room"/>
<foreign-key name="HOTEL_ROOMS_FK" delete-action="cascade"/>
</field>
</class>
So we now have given the datastore control over the cascade deletion strategy for objects stored in
these tables. Please be aware that JDO2 provides Dependent Fields as a way of allowing cascade
deletion. The difference here is that Dependent Fields is controlled by DataNucleus, whereas foreign
key delete actions are controlled by the datastore (assuming the datastore supports it even)
DataNucleus provides an extension that can give significant benefit to users. This is provided via the
PersistenceManagerFactory datanucleus.rdbms.constraintCreateMode. This property has 2 values.
The default is DataNucleus which will automatically decide which foreign keys are required to satisfy
the relationships that have been specified, whilst utilising the information provided in the MetaData
for foreign keys. The other option is JDO2 which will simply create foreign keys that have been
specified in the MetaData file(s).
Note that the foreign-key for a 1-N FK relation can be specified as above, or under the element
element. Note that the foreign-key for a 1-N Join Table relation is specified under field for the FK
from owner to join table, and is specified under element for the FK from join table to element table.
In the special case of application-identity and inheritance there is a foreign-key from subclass to
superclass. You can define this as follows
<class name="MySubClass">
<inheritance>
<join>
<foreign-key name="ID_FK"/>
</join>
</inheritance>
</class>
See also :-
• MetaData reference for <foreignkey> element
• Annotations reference for @ForeignKey
• Deletion of related objects using FK constraints
Applicable to RDBMS
In RDBMS datastores, it is accepted as good practice to have a primary key on all tables. You specify
in other parts of the MetaData which fields are part of the primary key (if using applicatioin identity),
or you define the name of the column DataNucleus should use for the primary key (if using datastore
identity). What these other parts of the MetaData don't allow is specifying the constraint name for the
primary key. You can specify this if you wish, like this
<class name="Booking">
<primary-key name="BOOKING_PK"/>
...
</class>
When the schema is generated for this table, the primary key will be given the specified name, and
will use the column(s) specified by the identity type in use.
In the case where you have a 1-N/M-N relation using a join table you can specify the name of the
primary key constraint used as follows
<class name="Hotel">
<field name="rooms">
<collection element-type="com.mydomain.samples.hotel.Room"/>
<join>
<primary-key name="HOTEL_ROOM_PK"/>
</join>
</field>
</class>
79 Enhancer
.......................................................................................................................................
• Post-compilation
• Runtime Enhancement
• Programmatically via an API
79.1.1 Maven
Maven operates from a series of plugins. There is a DataNucleus plugin for Maven that allows
enhancement of classes. Go to the Download section of the website and download this. Once you
have the Maven plugin, you then need to set any properties for the plugin in your pom.xml file. Some
properties that you may need to change are below
You will need to add datanucleus-api-jdo.jar into the CLASSPATH (of the plugin, or your project)
for the enhancer to operate. Also if using JPA metadata then you also will need datanucleus-api-
jpa.jar and persistence-api.jar in the CLASSPATH. You then run the Maven DataNucleus plugin, as
follows
mvn datanucleus:enhance
This will enhance all classes found that correspond to the classes defined in the JDO files in your
source tree. If you want to check the current status of enhancement you can also type
mvn datanucleus:enhance-check
<build>
...
<plugins>
<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-maven-plugin</artifactId>
<version>4.0.0-release</version>
<configuration>
<log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
...
</build>
So you then get auto-enhancement after each compile. Please refer to the Maven JDO guide for more
details.
79.1.2 Ant
Ant provides a powerful framework for performing tasks and DataNucleus provides an Ant task
to enhance classes. The DataNucleus Enhancer is in datanucleus-core.jar, and you need to make
sure that the datanucleus-core.jar, datanucleus-api-jdo.jar, jdo-api.jar (and optionally log4j.jar)
are in your CLASSPATH. If using JPA metadata then you will also need persistence-api.jar and
datanucleus-api-jpa.jar in the CLASSPATH. In the DataNucleus Enhancer Ant task, the following
parameters are available
The enhancer task extends the Apache Ant Java task, thus all parameters available to the Java task are
also available to the enhancer task.
So you could define something like the following, setting up the parameters enhancer.classpath,
jdo.file.dir, and log4j.config.file to suit your situation (the jdo.file.dir is a directory containing the
JDO files defining the classes to be enhanced). The classes specified by the XML Meta-Data files,
together with the XML Meta-Data files must be in the CLASSPATH (Please note that a CLASSPATH
should contain a set of JAR's, and a set of directories. It should NOT explictly include class files, and
should NOT include parts of the package names. If in doubt please consult a Java book).
<datanucleusenhancer classpathref="enhancer.classpath"
dir="${jdo.file.dir}" failonerror="true" verbose="true">
<jvmarg line="-Dlog4j.configuration=${log4j.config.file}"/>
</datanucleusenhancer>
</target>
You can also define the files to be enhanced using a fileset. When a fileset is defined, the Enhancer
Ant Task will not scan for additional files, and the option filesuffixes is ignored.
<datanucleusenhancer
dir="${jdo.file.dir}" failonerror="true" verbose="true">
<fileset dir="${classes.dir}">
<include name="**/*.jdo"/>
<include name="**/acme/annotated/persistentclasses/*.class"/>
</fileset>
<classpath>
<path refid="enhancer.classpath"/>
</classpath>
</datanucleusenhancer>
</target>
You can disable the enhancement execution upon the existence of a property with the usage of the if
parameter.
<datanucleusenhancer classpathref="enhancer.classpath"
dir="${jdo.file.dir}" failonerror="true" verbose="true">
<jvmarg line="-Dlog4j.configuration=${log4j.config.file}"/>
</datanucleusenhancer>
</target>
79.1.3 Manually
DataNucleus provides an Enhancer in datanucleus-core.jar. If you are building your application
manually and want to enhance your classes you follow the instructions in this section. You invoke the
enhancer as follows
where "mapping-files" and "class-files" are provided when not enhancing a persistence-unit,
and give the paths to the mapping files and class-files that define the classes being enhanced.
The input to the enhancer should be either a set of MetaData/class files or the name of the
"persistence-unit" to enhance. In the first option, if any classes have annotations then they must be
specified. All classes and MetaData files should be in the CLASSPATH when enhancing. To give an
example of how you would invoke the enhancer
Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/jdo-api.jar:
lib/datanucleus-api-jdo.jar:lib/log4j.jar
-Dlog4j.configuration=file:log4j.properties
org.datanucleus.enhancer.DataNucleusEnhancer
**/*.jdo
Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\jdo-api.jar;
lib\datanucleus-api-jdo.jar;lib\log4j.jar
-Dlog4j.configuration=file:log4j.properties
org.datanucleus.enhancer.DataNucleusEnhancer
target/classes/org/mydomain/mypackage1/package.jdo
So you pass in your JDO MetaData files (and/or the class files wihich use annotations) as the final
argument(s) in the list, and include the respective JAR's in the classpath (-cp). The enhancer responds
as follows
If you have errors here relating to "Log4J" then you must fix these first. If you receive no output about
which class was ENHANCED then you should look in the DataNucleus enhancer log for errors. The
enhancer performs much error checking on the validity of the passed MetaData and the majority of
errors are caught at this point. You can also use the DataNucleus Enhancer to check whether classes
are enhanced. To invoke the enhancer in this mode you specify the checkonly flag. This will return
a list of the classes, stating whether each class is enhanced for persistence under JDO or not. The
classes need to be in the CLASSPATH (Please note that a CLASSPATH should contain a set of
JAR's, and a set of directories. It should NOT explictly include class files, and should NOT include
parts of the package names. If in doubt please consult a Java book).
The statement above will mean that all classes, when being loaded, will be processed by the
ClassFileTransformer (except class in packages "java.*", "javax.*", "org.datanucleus.*"). This means
that it can be slow since the MetaData search algorithm will be utilised for each. To speed this up
you can specify an argument to that command specifying the names of package(s) that should be
processed (and all others will be ignored). Like this
so in this case only classes being loaded that are in mydomain.mypackage1 and
mydomain.mypackage2 will be attempted to be enhanced.
Please take care over the following when using runtime enhancement
• When you have a class with a field of another persistable type make sure that you mark that field
as "persistent" (@Persistent, or in XML) since with runtime enhancement at that point the related
class is likely not yet enhanced so will likely not be marked as persistent otherwise. Be explicit
• If the agent jar is not found make sure it is specified with an absolute path.
import javax.jdo.JDOEnhancer;
This will look in META-INF/persistence.xml and enhance all classes defined by that unit. Please note
that you will need to load the enhanced version of the class into a different ClassLoader after
performing this operation to use them. See this guide
DataNucleus requires that all classes that are persisted implement Persistable. Why should we do
this, Hibernate/TopLink dont need it ?. Well thats a simple question really
• DataNucleus uses this Persistable interface, and adds it using bytecode enhancement techniques
so that you never need to actually change your classes. This means that you get transparent
persistence, and your classes always remain your classes. ORM tools that use a mix of reflection
and/or proxies are not totally transparent.
• DataNucleus' use of Persistable provides transparent change tracking. When any change is
made to an object the change creates a notification to DataNucleus allowing it to be optimally
persisted. ORM tools that dont have access to such change tracking have to use reflection to
detect changes. The performance of this process will break down as soon as you read a large
number of objects, but modify just a handful, with these tools having to compare all object states
for modification at transaction commit time.
Why not also read this comparison of bytecode enhancement, and proxies. It gives a clear enough
comparison of the relative benefits.
In a JDO-enabled application there are 3 categories of classes. These are persistable,
PersistenceAware and normal classes. The Meta-Data defines which classes fit into these categories.
To give an example for JDO, we have 3 classes. The class A is to be persisted in the datastore. The
class B directly updates the fields of class A but doesn't need persisting. The class C is not involved in
the persistence process. We would define JDO MetaData for these classes like this
So our MetaData is mainly for those classes that are persistable and are to be persisted to the
datastore (we don't really need the persistence-modifier for thse classes since this is the default). For
PersistenceAware classes we simply notate that the class knows about persistence. We don't define
MetaData for any class that has no knowledge of persistence.
JDO allows implementations to bytecode enhance persistable classes to implement some interface
to provide them with change tracking etc. JDO provides a builtin PersistenceCapable interface
but we don't use that so we have full control over what information is stored in the class. Users
could manually make their classes implement this Persistable interface but this would impose work
on them. JDO permits the use of a byte-code enhancer that converts the users normal classes to
implement this interface. DataNucleus provides its own byte-code enhancer (in the datanucleus-
core.jar). This section describes how to use this enhancer with DataNucleus.
The example above doesn't show all Persistable methods, but demonstrates that all added methods
and fields are prefixed with "dn" to distinguish them from the users own methods and fields. Also
each persistent field of the class will be given a dnGetXXX, dnSetXXX method so that accesses of
these fields are intercepted so that DataNucleus can manage their "dirty" state.
The MetaData defines which classes are required to be persisted, and also defines which
aspects of persistence each class requires. For example if a class has the detachable
attribute set to true, then that class will be enhanced to also implement Detachable
Again, the example above doesn't show all methods added for the Detachable interface but the main
thing to know is that the detached state (object id of the datastore object, the version of the datastore
object when it was detached, and which fields were detached is stored in "dnDetachedState"). Please
see the JDO spec for more details.
If the MetaData is changed in any way during development, the classes should always be
recompiled and re-enhanced afterwards.
• Slows down the code-test cycle. This is erroneous since you only need to enhance just before test
and the provided tools for Ant, Eclipse and Maven all do the enhancement job automatically and
rapidly.
• Is less "lazy" than the proxy approach since you have to load the object as soon as you get a
pointer to it. In a 1-1 relation you have to load the object then since you would cause issues with
null pointers otherwise. With 1-N relations you load the elements of the collection/map only
when you access them and not the collection/map. Hardly an issue then is it!
• Fail to detect changes to public fields unless you enhance your client code. Firstly very few
people will be writing code with public fields since it is bad practice in an OO design, and
secondly, this is why we have "PersistenceAware" classes.
So as you can see, there are no valid reasons against byte-code enhancement, and the pluses are that
runtime detection of dirty events on objects is much quicker, hence your persistence layer operates
faster without any need for iterative reflection-based checks. The fact is that Hibernate itself also now
has a mode whereby you can do bytecode enhancement although not the default mode of Hibernate.
So maybe it wasn't so evil after all ?
79.2.2 Decompilation
Many people will wonder what actually happens to a class upon bytecode enhancement. In simple
terms the necessary methods and fields are added so as to implement Persistable. If you want to check
this, just use a Java decompiler such as JD. It has a nice GUI allowing you to just select your class to
decompile and shows you the source.
org.datanucleus.util.DetachListener.setInstance(myListener);
80 Datastore Schema
.......................................................................................................................................
If you want to create the schema ("tables"+"columns"+"constraints") during the persistence process,
the property datanucleus.schema.autoCreateAll provides a way of telling DataNucleus to do this.
It's a shortcut to setting the other 3 properties to true. Thereafter, during calls to DataNucleus to
persist classes or performs queries of persisted data, whenever it encounters a new class to persist that
it has no information about, it will use the MetaData to check the datastore for presence of the "table",
and if it doesn't exist, will create it. In addition it will validate the correctness of the table (compared
to the MetaData for the class), and any other constraints that it requires (to manage any relationships).
If any constraints are missing it will create them.
• If you wanted to only create the "tables" required, and none of the "constraints" the property
datanucleus.schema.autoCreateTables provides this, simply performing the tables part of the
above.
• If you want to create any missing "columns" that are required, the property
datanucleus.schema.autoCreateColumns provides this, validating and adding any missing
columns.
• If you wanted to only create the "constraints" required, and none of the "tables" the property
datanucleus.schema.autoCreateConstraints provides this, simply performing the "constraints"
part of the above.
• If you want to keep your schema fixed (i.e don't allow any modifications at runtime) then make
sure that the properties datanucleus.schema.autoCreate{XXX} are set to false
(other values drop which deletes the schema, and drop-and-create which deletes and recreates the
schema). When you create your PMF it will generate the schema before it returns your PMF.
You have some additional control over whether to actually create the schema, or whether to
just output DDL for the schema (which you could apply yourself) - see the persistence property
datanucleus.generateSchema.target which can be set to scripts to just create the DDL.
For RDBMS there are a few extensions here that are also worthy of mention, in that you can define
scripts that are run during this schema generation phase. These are controlled by the following
persistence properties
DataNucleus can check any existing schema against what is implied by the MetaData.
The property datanucleus.schema.validateTables provides a way of telling DataNucleus to validate
any tables that it needs against their current definition in the datastore. If the user already has a
schema, and want to make sure that their tables match what DataNucleus requires (from the MetaData
definition) they would set this property to true. This can be useful for example where you are trying to
map to an existing schema and want to verify that you've got the correct MetaData definition.
The property datanucleus.schema.validateColumns provides a way of telling DataNucleus to
validate any columns of the tables that it needs against their current definition in the datastore. If the
user already has a schema, and want to make sure that their tables match what DataNucleus requires
(from the MetaData definition) they would set this property to true. This will validate the precise
column types and widths etc, including defaultability/nullability settings. Please be aware that many
JDBC drivers contain bugs that return incorrect column detail information and so having this
turned off is sometimes the only option (dependent on the JDBC driver quality).
The property datanucleus.schema.validateConstraints provides a way of telling DataNucleus
to validate any constraints (primary keys, foreign keys, indexes) that it needs against their current
definition in the datastore. If the user already has a schema, and want to make sure that their table
constraints match what DataNucleus requires (from the MetaData definition) they would set this
property to true.
This will mean that all RDBMS DDL and SQL statements will prefix table names with the necessary
catalog and schema names (specify which ones your datastore supports).
Note that the values of the position start at 0, and should be specified completely for all columns of all
fields.
80.1.6 Read-Only
If your datastore is read-only (you can't add/update/delete any data in it), obviously you could just
configure your application to not perform these operations. An alternative is to set the PMF as "read-
only". You do this by setting the persistence property javax.jdo.option.ReadOnly to true.
From now on, whenever you perform a persistence operation that implies a change in datastore data,
the operation will throw a JDOReadOnlyException.
DataNucleus provides an additional control over the behaviour when an attempt is made to change
a read-only datastore. The default behaviour is to throw an exception. You can change this using the
persistence property datanucleus.readOnlyDatastoreAction with values of "EXCEPTION" (default),
and "IGNORE". "IGNORE" has the effect of simply ignoring all attempted updates to readonly
objects.
You can take this read-only control further and specify it just on specific classes. Like this
80.2 SchemaTool
DataNucleus SchemaTool currently works with RDBMS, HBase, Excel, OOXML, ODF, MongoDB,
Cassandra datastores and is very simple to operate. It has the following modes of operation :
• createSchema - create the specified schema if the datastore supports that operation.
• deleteSchema - delete the specified schema if the datastore supports that operation.
• create - create all database tables required for the classes defined by the input data.
• delete - delete all database tables required for the classes defined by the input data.
• deletecreate - delete all database tables required for the classes defined by the input data, then
create the tables.
• validate - validate all database tables required for the classes defined by the input data.
• dbinfo - provide detailed information about the database, it's limits and datatypes support. Only
for RDBMS currently.
• schemainfo - provide detailed information about the database schema. Only for RDBMS
currently.
Note that for RDBMS, the create/ delete modes can also be used by adding "-ddlFile {filename}" and
this will then not create/delete the schema, but instead output the DDL for the tables/constraints into
the specified file.
For the create, delete and validate modes DataNucleus SchemaTool accepts either of the following
types of input.
• A set of MetaData and class files. The MetaData files define the persistence of the classes they
contain. The class files are provided when the classes have annotations.
• The name of a persistence-unit. The persistence-unit name defines all classes, metadata
files, and jars that make up that unit. Consequently, running DataNucleus SchemaTool with a
persistence unit name will create the schema for all classes that are part of that unit.
Here we provide many different ways to invoke DataNucleus SchemaTool
80.2.1 Maven
If you are using Maven to build your system, you will need the DataNucleus Maven plugin.
This provides 5 goals representing the different modes of DataNucleus SchemaTool. You can
use the goals datanucleus:schema-create, datanucleus:schema-delete, datanucleus:schema-
validate depending on whether you want to create, delete or validate the database tables. To use the
DataNucleus Maven plugin you will may need to set properties for the plugin (in your pom.xml). For
example
<build>
...
<plugins>
<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-maven-plugin</artifactId>
<version>4.0.0-release</version>
<configuration>
<props>${basedir}/datanucleus.properties</props>
<log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
<verbose>true</verbose>
</configuration>
</plugin>
</plugins>
...
</build>
So with these properties when I run SchemaTool it uses properties from the file
datanucleus.properties at the root of the Maven project. I am also specifying a log4j configuration file
defining the logging for the SchemaTool process. I then can invoke any of the Maven goals
80.2.2 Ant
An Ant task is provided for using DataNucleus SchemaTool. It has classname
org.datanucleus.store.schema.SchemaToolTask, and accepts the following parameters
The SchemaTool task extends the Apache Ant Java task, thus all parameters available to the Java task
are also available to the SchemaTool task.
In addition to the parameters that the Ant task accepts, you will need to set up your CLASSPATH
to include the classes and MetaData files, and to define the following system properties via the
sysproperty parameter (not required when specifying the persistence props via the properties file, or
when providing the persistence-unit)
So you could define something like the following, setting up the parameters
schematool.classpath, datanucleus.ConnectionDriverName, datanucleus.ConnectionURL,
datanucleus.ConnectionUserName, and datanucleus.ConnectionPassword to suit your situation.
You define the jdo files to create the tables using fileset.
All classes, MetaData files, "persistence.xml" files must be present in the CLASSPATH. In
terms of the schema to use, you either specify the "props" file (recommended), or you specify the
System properties defining the database connection, or the properties in the "persistence-unit". You
should only specify one of the [modes] above. Let's make a specific example and see the output from
SchemaTool. So we have the following files in our application
So we want to create the schema for our persistent classes. So let's invoke DataNucleus SchemaTool
to do this, from the top level of our project. In this example we're using Linux (change the
CLASSPATH definition to suit for Windows)
So as you see, DataNucleus SchemaTool prints out our input, the properties used, and finally
a success message. If an error occurs, then something will be printed to the screen, and more
information will be written to the log.
package org.datanucleus.store.schema;
So for example to create the schema for classes mydomain.A and mydomain.B you would do
something like this
JDOPersistenceManagerFactory pmf =
(JDOPersistenceManagerFactory)JDOHelper.getPersistenceManagerFactory("datanucleus.properties");
NucleusContext ctx = pmf.getNucleusContext();
...
List classNames = new ArrayList();
classNames.add("mydomain.A");
classNames.add("mydomain.B");
try
{
Properties props = new Properties();
// Set any properties for schema generation
((SchemaAwareStoreManager)ctx.getStoreManager()).createSchemaForClasses(classNames, props);
}
catch(Exception e)
{
...
}
81 Bean Validation
.......................................................................................................................................
The Bean Validation API (JSR0303) can be hooked up with JDO (DataNucleus extension) so that you
have validation of an objects values prior to persistence, update and deletion. To do this
• Put the javax.validation "validation-api" jar in your CLASSPATH, along with the Bean
Validation implementation jar of your choice (Apache BVAL, Hibernate Validator, etc)
• Set the persistence property datanucleus.validation.mode to one of auto, none (default), or
callback
• Optionally set the persistence property(s) datanucleus.validation.group.pre-persist,
datanucleus.validation.group.pre-update, datanucleus.validation.group.pre-remove to fine tune
the behaviour (the default is to run validation on pre-persist and pre-update if you don't specify
these).
• Use JDO as you normally would for persisting objects
To give a simple example of what you can do with the Bean Validation API
@PersistenceCapable
public class Person
{
@PrimaryKey
@NotNull
private Long id;
@NotNull
@Size(min = 3, max = 80)
private String name;
...
}
So we are validating that instances of the Person class will have an "id" that is not null and that the
"name" field is not null and between 3 and 80 characters. If it doesn't validate then at persist/update an
exception will be thrown.
A further use of the Bean Validation annotations @Size(max=...) and @NotNull is that if you specify
these then you have no need to specify the equivalent JDO "length" and "allowsNull" attributes since
they equate to the same thing.
82 PersistenceManagerFactory
.......................................................................................................................................
A slight variation on this, is to have a file to specify these properties like this
javax.jdo.PersistenceManagerFactoryClass=org.datanucleus.api.jdo.JDOPersistenceManagerFactory
javax.jdo.option.ConnectionURL=jdbc:mysql://localhost/myDB
javax.jdo.option.ConnectionDriverName=com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName=login
javax.jdo.option.ConnectionPassword=password
or if the above file is in the CLASSPATH (at "datanucleus.properties" in the root of the
CLASSPATH), then
If using a named PMF file, you can create the PMF by providing the name of the PMF like this
If using a META-INF/persistence.xml file, you can simply specify the persistence-unit name as
Another alternative, when specifying your datastore via JNDI, would be to call
JDOHelper.getPersistenceManagerFactory(jndiLocation, context);, and then set the other persistence
properties on the received PMF.
Whichever way we wish to obtain the PersistenceManagerFactory we have defined a series of
properties to give the behaviour of the PersistenceManagerFactory. The first property specifies to
use the DataNucleus implementation, and the following 4 properties define the datastore that it should
connect to. There are many properties available. Some of these are standard JDO properties, and some
are DataNucleus extensions.
DataNucleus provides many properties to extend the control that JDO gives you. These can be used
alongside the above standard JDO properties, but will only work with DataNucleus. Please consult the
Persistence Properties Guide for full details.
When designing an application you can usually nicely separate your persistable objects into
independent groupings that can be treated separately, perhaps within a different DAO object, if using
DAOs. JDO uses the (JPA) idea of a persistence-unit. A persistence-unit provides a convenient way
of specifying a set of metadata files, and classes, and jars that contain all classes to be persisted in a
grouping. The persistence-unit is named, and the name is used for identifying it. Consequently this
name can then be used when defining what classes are to be enhanced, for example.
To define a persistence-unit you first need to add a file persistence.xml to the META-INF/ directory
of the CLASSPATH (this may mean WEB-INF/classes/META-INF when using a web-application).
This file will be used to define your persistence-units. Lets show an example
</persistence>
In this example we have defined 2 persistence-units. The first has the name "OnlineStore" and
contains 5 classes (annotated). The second has the name "Accounting" and contains a metadata file
called "package.jdo" in a particular package (which will define the classes being part of that unit).
This means that once we have defined this we can reference these persistence-units in our persistence
operations. You can find the XSD for persistence.xml here.
DataNucleus allows an extension to JDO to dynamically create persistence-units at runtime. Use the
following code sample as a guide. Obviously any classes defined in the persistence-unit need to have
been enhanced.
import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
It should be noted that if you call pumd.toString(); then this returns the text that would have been
found in a persistence.xml file.
Typically applications create one PMF per datastore being utilised. An alternate to persistence-unit is
to use a named PMF, defined in a file META-INF/jdoconfig.xml at the root of the CLASSPATH (this
</jdoconfig>
So in this example we have 2 named PMFs. The first is known by the name "Datastore" and utilises
datastore transactions. The second is known by the name "Optimistic" and utilises optimistic
transactions. You simply define all properties for the particular PMF within its specification block.
And finally we instantiate our PMF like this
That's it. The PMF we are returned from JDOHelper will have all of the properties defined in META-
INF/jdoconfig.xml under the name of "Optimistic".
83 L2 Cache
.......................................................................................................................................
• Level 1 Cache - mandated by the JDO specification, and represents the caching of instances
within a PersistenceManager
• Level 2 Cache - represents the caching of instances within a PersistenceManagerFactory (across
multiple PersistenceManager's)
You can think of a cache as a Map, with values referred to by keys. In the case of JDO, the key is the
object identity (identity is unique in JDO).
The Level 2 cache is a DataNucleus plugin point allowing you to provide your own cache where you
require it. Use the examples of the EHCache, Coherence caches etc as reference.
Note that you can have a PMF with L2 caching enabled yet have a PM with it disabled. This is
achieved by creating the PM as you would normally, and then call
pm.setProperty("datanucleus.cache.level2.type", "none");
provides methods to control the retention of objects in the cache. You have 3 groups of methods
• evict - used to remove objects from the Level 2 Cache
• pin - used to pin objects into the cache, meaning that they will not get removed by garbage
collection, and will remain in the Level 2 cache until removed.
• unpin - used to reverse the effects of pinning an object in the Level 2 cache. This will mean that
the object can thereafter be garbage collected if not being used.
These methods can be called to pin objects into the cache that will be much used. Clearly this will
be very much application dependent, but it provides a mechanism for users to exploit the caching
features of JDO. If an object is not "pinned" into the L2 cache then it can typically be garbage
collected at any time, so you should utilise the pinning capability for objects that you wish to retain
access to during your application lifetime. For example, if you have an object that you want to be
found from the cache you can do
pm.makePersistent(myObject);
// "myObject" will now be pinned since we are pinning all objects of type MyClass.
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.close();
}
}
Thereafter, whenever something refers to myObject, it will find it in the Level 2 cache. To turn this
behaviour off, the user can either unpin it or evict it.
JDO allows control over which classes are put into a Level 2 cache. You do this by specifying the
cacheable attribute to false (defaults to true). So with the following specification, no objects of type
MyClass will be put in the L2 cache.
Using XML:
<class name="MyClass" cacheable="false">
...
</class>
Using Annotations:
@Cacheable("false")
public class MyClass
{
...
}
JDO allows you control over which fields of an object are put in the Level 2 cache. You do this by
specifying the cacheable attribute to false (defaults to true). This setting is only required for fields
that are relationships to other persistable objects. Like this
Using XML:
<class name="MyClass">
<field name="values"/>
<field name="elements" cacheable="false"/>
...
</class>
Using Annotations:
public class MyClass
{
...
Collection values;
@Cacheable("false")
Collection elements;
}
So in this example we will cache "values" but not "elements". If a field is cacheable then
• If it is a persistable object, the "identity" of the related object will be stored in the Level 2 cache
for this field of this object
• If it is a Collection of persistable elements, the "identity" of the elements will be stored in the
Level 2 cache for this field of this object
• If it is a Map of persistable keys/values, the "identity" of the keys/values will be stored in the
Level 2 cache for this field of this object
When pulling an object in from the Level 2 cache and it has a reference to another object Access
Platform uses the "identity" to find that object in the Level 1 or Level 2 caches to re-relate the objects.
DataNucleus has an extension in metadata allowing the user to define that all instances of a class are
automatically pinned in the Level 2 cache.
@PersistenceCapable
@Extension(vendorName="datanucleus", key="cache-pin", value="true")
public class MyClass
{
...
}
datanucleus.cache.level2.type=javax.cache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.timeout={expiration time in millis - optional}
datanucleus.cache.level2.type=jcache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.timeout={expiration time in millis - optional}
datanucleus.cache.level2.type=coherence
datanucleus.cache.level2.cacheName={coherence cache name}
The Coherence cache name is the name that you would normally put into a call to
CacheFactory.getCache(name). As mentioned earlier, this cache does not support the pin/unpin
operations found in the standard JDO interface. However you do have the benefits of Oracle's
distributed/serialized caching. If you require more control over the Coherence cache whilst using it
with DataNucleus, you can just access the cache directly via
datanucleus.cache.level2.type=ehcache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.configurationFile={EHCache configuration file (in classpath)}
The EHCache plugin also provides an alternative L2 Cache that is class-based. To use this you would
need to replace "ehcache" above with "ehcacheclassbased".
datanucleus.cache.level2.type=oscache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.type=swarmcache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.type=cacheonix
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.timeout={timeout-in-millis (default=60)}
datanucleus.cache.level2.configurationFile={Cacheonix configuration file (in classpath)}
<?xml version="1.0"?>
<cacheonix>
<local>
<!-- One cache per class being stored. -->
<localCache name="mydomain.MyClass">
<store>
<lru maxElements="1000" maxBytes="1mb"/>
<expiration timeToLive="60s"/>
</store>
</localCache>
<!-- Fallback cache for classes indeterminable from their id. -->
<localCache name="datanucleus">
<store>
<lru maxElements="1000" maxBytes="10mb"/>
<expiration timeToLive="60s"/>
</store>
</localCache>
</cacheonix>
84 Auto-Start
.......................................................................................................................................
By default with JDO implementations when you open a PersistenceManagerFactory and obtain
a PersistenceManager DataNucleus knows nothing about which classes are to be persisted to that
datastore. JDO implementations only load the Meta-Data for any class when the class is first enlisted
in a PersistenceManager operation. For example you call makePersistent on an object. The first time
a particular class is encountered DataNucleus will dynamically load the Meta-Data for that class. This
typically works well since in an application in a particular operation the PersistenceManagerFactory
may well not encounter all classes that are persistable to that datastore. The reason for this dynamic
loading is that JDO implementations can't be expected to scan through the whole Java CLASSPATH
for classes that could be persisted there. That would be inefficient.
There are situations however where it is desirable for DataNucleus to have knowledge about what is
to be persisted, or what subclasses of a candidate are possible on executing a query, so that it can load
the Meta-Data at initialisation of the persistence factory and hence when the classes are encountered
for the first time nothing needs doing. There are several ways of achieving this
• Define your classes/MetaData in a Persistence Unit and when the PersistenceManagerFactory
is initialised it loads the persistence unit, and hence the MetaData for the defined classes and
mapping files. This is described on the linked page
• Put the package.jdo at the root of the CLASSPATH, containing all classes, and when the first
class is encountered it searches for its metadata, encounters and parses the root package.jdo, and
consequently loads the metadata for all classes
• Use a DataNucleus extension known as Auto-Start Mechanism. This is set with the persistence
property datanucleus.autoStartMechanism. This can be set to None, XML, Classes, MetaData.
In addition we have SchemaTable for RDBMS datastores. These are described below.
<datanucleus_autostart>
<class name="mydomain.MyClass" table="MY_TABLE_1" type="FCO" version="3.1.1"/>
</datanucleus_autostart>
• Checked will mean that DataNucleus will throw an exception and the user will be expected to
manually fix their database mismatch (perhaps by removing the existing tables).
• Quiet (the default) will simply remove the entry from NUCLEUS_TABLES and continue
without exception.
• Ignored will simply continue without doing anything.
The default database schema used the SchemaTable is described below:
TABLE : NUCLEUS_TABLES
(
COLUMN : CLASS_NAME VARCHAR(128) PRIMARY KEY, -- Fully qualified persistent Class name
COLUMN : TABLE_NAME VARCHAR(128), -- Table name
COLUMN : TYPE VARCHAR(4), -- FCO | SCO
COLUMN : OWNER VARCHAR(2), -- 1 | 0
COLUMN : VERSION VARCHAR(20), -- DataNucleus version
COLUMN : INTERFACE_NAME VARCHAR(255) -- Fully qualified persistent Class type
-- of the persistent Interface implemented
)
If you want to change the table name (from NUCLEUS_TABLES) you can set the persistence
property datanucleus.rdbms.schemaTable.tableName
85 Data Federation
.......................................................................................................................................
javax.jdo.option.ConnectionDriverName=com.mysql.jdbc.Driver
javax.jdo.option.ConnectionURL=jdbc:mysql://127.0.0.1/nucleus?useServerPrepStmts=false
javax.jdo.option.ConnectionUserName=mysql
javax.jdo.option.ConnectionPassword=
datanucleus.datastore.store2=datanucleus2.properties
You note that this refers to a store2, which is defined by datanucleus2.properties. So the secondary
datastore is defined by
javax.jdo.option.ConnectionURL=mongodb:/nucleus
@PersistenceCapable
@Extension(vendorName="datanucleus", key="datastore", value="store2")
public class MyOtherClass
{
...
}
So for any persistence of objects of type MyOtherClass, they will be persisted into the MongoDB
secondary datastore.
86 PersistenceManager
.......................................................................................................................................
as follows
PersistenceManager pm = pmf.getPersistenceManager();
You likely will be performing all operations on a PersistenceManager within a transaction, whether
your transactions are controlled by your JavaEE container, by a framework such as Spring, or by
locally defined transactions. Alternatively you can perform your operations non-transactional. In the
examples below we will omit the transaction demarcation for clarity.
This will result in the object being persisted into the datastore, though clearly it will not be persistent
until you commit the transaction. The LifecycleState of the object changes from Transient to
PersistentClean (after makePersistent), to Hollow (at commit).
Alternatively by calling
Object id = pm.newObjectIdInstance(cls, key);
So what ? Well the identity can be used to retrieve the object again at some other part in your
application. So you pass the identity into your application, and the user clicks on some button on a
web page and that button corresponds to a particular object identity. You can then go back to your
data layer and retrieve the object as follows
Object obj = pm.getObjectById(id);
A DataNucleus extension is to pass in a String form of the identity to the above method. It accepts
identity strings of the form
• {fully-qualified-class-name}:{key}
• {discriminator-name}:{key}
where the key is the identity value (datastore-identity) or the result of PK.toString() (application-
identity). So for example we could input
obj = pm.getObjectById("mydomain.MyClass:3");
When you call the method getObjectById if an object with that identity is found in the cache then a
call is, by default, made to validate it still exists. You can avoid this call to the datastore by setting the
persistence property datanucleus.findObject.validateWhenCached to false.
where 123 is the value of the primary key field (numeric). Note that the first argument could be a base
class and the real object could be an instance of a subclass of that.
Don't forget that you can also use deletion by query to delete objects. Alternatively use bulk deletion.
Object detachedObj = pm.detachCopy(obj); // Returns a copy of the persisted object, in detached state
The detached object is like the original object except that it has no StateManager connected, and it
stores its JDO identity and version. It retains a list of all fields that are modified while it is detached.
This means that when you want to "attach" it to the data-access layer it knows what to update.
As an alternative, to make the detachment process transparent, you can set the PMF property
datanucleus.DetachAllOnCommit to true and when you commit your transaction all objects enlisted in
the transaction will be detached.
Object attachedObj = pm.makePersistent(obj); // Returns a copy of the detached object, in attached state
• none - will turn off L1 caching. Only ever use this where the cache is of no use and you are
performing bulk operations and not requiring objects returned
You can specify the type of L1 cache by providing the persistence property
datanucleus.cache.level1.type. You set this to the value of the type required. If you want to remove
objects from the L1 cache programmatically you should use the pm.evict or pm.evictAll methods.
Objects are placed in the L1 cache (and updated there) during the course of the transaction. This
provides rapid access to the objects in use in the users application and is used to guarantee that there
is only one object with a particular identity at any one time for that PersistenceManager. When the
PersistenceManager is closed the L1 cache is cleared.
The L1 cache is a DataNucleus plugin point allowing you to provide your own cache where you
require it.
87 PM Proxy
.......................................................................................................................................
88 Object Lifecycle
.......................................................................................................................................
So a newly created object is transient. You then persist it and it becomes persistent. You then
commit the transaction and it is detached for use elsewhere in the application. You then attach any
changes back to persistence and it becomes persistent again. Finally when you delete the object from
persistence and commit that transaction it is in transient state.
An alternative JDO lifecycle occurs when you have DetachAllOnCommit as false. Now at commit the
object moves into hollow state (still has its identity, but its field values are optionally unloaded). Set
the persistence property datanucleus.RetainValues to not unset the values of any non-primary-key
fields when migrating to hollow state.
With JDO there are actually some additional lifecycle states, notably when an object has a field
changed, becoming dirty, so you get an object in "persistent-dirty", "detached-dirty" states for
example. The average user doesn't need to know about these so we don't cover them here. To inspect
the lifecycle state of an object, simply call
JDOHelper.getObjectState(obj);
See also :-
• Attach/Detach of objects
These methods returns the names of the dirty/loaded fields in the supplied object. The pm argument is
only required if the object is detached
These methods returns whether the specified field in the supplied object is dirty/loaded. The pm
argument is only required if the object is detached
Method /
Current P-New- P- P-
State T-Clean T-Dirty P-New P-Clean P-Dirty Hollow Deleted Deleted Nontrans
pm.makePersistent
P-New P-New no no no no no no no
change change change change change change change
pm.deletePersistent
error error P-New- P- P- P- no no P-
Deleted Deleted Deleted Deleted change change Deleted
pm.makeTransactional
no no no no no P-Clean no no P-Clean
change change change change change change change
pm.makeNontransactional
no error error P- error no error error no
change Nontrans change change
pm.makeTransient
no no error T-Clean error T-Clean error error T-Clean
change change
tx.commit no T-Clean Hollow Hollow Hollow no T-Clean T-Clean no
retainValues=false
change change change
tx.commit no T-Clean P- P- P- no T-Clean T-Clean no
retainValues=true
change Nontrans Nontrans Nontrans change change
pm.detachCopy()
error Detached- Detached-
outside Clean Clean
txn,
Nontx-
read=true
pm.detachCopy()
error error Detached-
outside Clean
txn,
Nontx-
read=false
pm.detachCopy()
Detached- Detached- Detached- Detached- Detached- Detached- error error Detached-
active Clean Clean Clean Clean Clean Clean Clean
txn
89 Lifecycle Callbacks
.......................................................................................................................................
.
To give an example of this capability, let us define a class that needs to perform some operation just
before it's object is deleted.
So we have implemented InstanceCallbacks and have defined the 4 required methods. Only one of
these is of importance in this example.
These methods will be called just before storage in the data store ( jdoPreStore), just before clearing (
jdoPreClear), just after being loaded from the datastore ( jdoPostLoad) and just before being deleted (
jdoPreDelete).
and DetachCallback
. If you want to intercept attach/detach events your class can implement these interfaces. You will
then need to implement the following methods
.
To give an example of this capability, let us define a Listener for our persistence process.
Here we've provided a listener to receive events for CREATE, DELETE, LOAD, and STORE
of objects. These are the main event types and in our simple case above we will simply log
the event. All that remains is for us to register this listener with the PersistenceManager, or
PersistenceManagerFactory
pm.addInstanceLifecycleListener(new LoggingLifecycleListener(), null);
When using this interface the user should always remember that the listener is called within the
same transaction as the operation being reported and so any changes they then make to the objects in
question will be reflected in that objects state.
Register the listener with the PersistenceManager or PersistenceManagerFactory provide different
effects. Registering with the PersistenceManagerFactory means that all PersistenceManagers created
by it will have the listeners registered on the PersistenceManagerFactory called. Registering the
listener with the PersistenceManager will only have the listener called only on events raised only by
the PersistenceManager instance.
The above diagram displays the sequence of actions for a listener registered only in the
PersistenceManager. Note that a second PersistenceManager will not make calls to the listener
registered in the first PersistenceManager.
The above diagram displays the sequence of actions for a listener registered in the
PersistenceManagerFactory. All events raised in a PersistenceManager obtained
from the PersistenceManagerFactory will make calls to the listener registered in the
PersistenceManagerFactory.
DataNucleus supports the following instance lifecycle listener types
The default JDO2 lifecycle listener StoreLifecycleListener only informs the listener of the
object being stored. It doesn't provide information about the fields being stored in that event.
DataNucleus extends the JDO2 specification and on the "preStore" event it will return an
instance of org.datanucleus.api.jdo.FieldInstanceLifecycleEvent (which extends the JDO2
InstanceLifecycleEvent) and provides access to the names of the fields being stored.
/**
* Accessor for the field names affected by this event
* @return The field names
*/
public String[] getFieldNames()
...
}
If the store event is the persistence of the object then this will return all field names. If
instead just particular fields are being stored then you just receive those fields in the event.
So the only thing to do to utilise this DataNucleus extension is cast the received event to
org.datanucleus.FieldInstanceLifecycleEvent
90 Attach/Detach
.......................................................................................................................................
This acts as an instruction to the enhancement process to add methods necessary to utilise the attach/
detach process.
The following code fragment highlights how to use the attach/detach mechanism
Product working_product=null;
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
catch (Exception e)
{
// Handle the exception
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
...
tx.commit();
}
catch (Exception e)
{
// Handle the exception
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
So we now don't need to do any manual copying of object fields just using a simple call to detach the
object, and then attach it again later. Here are a few things to note with attach/detach :-
• Calling detachCopy on an object that is not detachable will return a transient instance that is a
COPY of the original, so use the COPY thereafter.
• Calling detachCopy on an object that is detachable will return a detached instance that is a
COPY of the original, so use this COPY thereafter
• A detached object retain the id of its datastore entity. Detached objects should be used where you
want to update the objects and attach them later (updating the associated object in the datastore.
If you want to create copies of the objects in the datastore with their own identities you should
use makeTransient instead of detachCopy.
• Calling detachCopy will detach all fields of that object that are in the current Fetch Group for
that class for that PersistenceManager.
• By default the fields of the object that will be detached are those in the Default Fetch Group.
• You should choose your Fetch Group carefully, bearing in mind which object(s) you want to
access whilst detached. Detaching a relation field will detach the related object as well.
• If you don't detach a field of an object, you cannot access the value for that field while the object
is detached.
• If you don't detach a field of an object, you can update the value for that field while detached,
and thereafter you can access the value for that field.
• Calling makePersistent will return an (attached) copy of the detached object. It will attach all
fields that were originally detached, and will also attach any other fields that were modified
whilst detached.
When attaching an object graph (using makePersistent()) DataNucleus will, by default, make
a check if each detached object has been detached from this datastore (since they could have
been detached from a different datastore). This clearly can cause significant numbers of
additional datastore activity with a large object graph. Consequently we provide a PMF property
datanucleus.attachSameDatastore which, when set to true, will omit these checks and assume that we
are attaching to the same datastore they were detached from.
To read more about attach/detach and how to use it with fetch-groups you can look at our Tutorial
on DAO Layer design.
// Create a PMF
...
// Create an object
MyObject my = new MyObject();
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
// The object "my" is now in detached state and can be used further
}
finally
{
if (tx.isActive)
{
tx.rollback();
}
}
pm.makePersistent(working_product);
// working_product is now in persistent (attached) state
tx.commit();
}
catch (Exception e)
{
// Handle the exception
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
Please note that if you try to attach two detached objects representing the same underlying persistent
object within the same transaction (i.e a persistent object with the same identity already exists in the
level 1 cache), then a JDOUserException will be thrown.
A backup to the above programmatic detachment of instances is that when you close your
PersistenceManager you can opt to have all instances currently cached in the Level 1 Cache
of that PersistenceManager detached automatically. This means that you can persist instances,
and then when you close the PM the instances will be detached and ready for further work. This
is a DataNucleus extension. It is recommended that you use "detachAllOnCommit" since
that is standard JDO and since this option will not work in J2EE environments where the
PersistenceManager close is controlled by the J2EE container
You enable this by setting the PersistenceManagerFactory (PMF) property
datanucleus.DetachOnClose when you create the PMF. Let's give an example
// Create an object
MyObject my = new MyObject();
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
pm.close();
// The object "my" is now in detached state and can be used further
}
finally
{
if (tx.isActive)
{
tx.rollback();
}
}
That is about as close to transparent persistence as you will find. When the PM is closed all instances
found in the L1 Cache are detached using the current FetchPlan, and so all fields in that plan for the
instances in question will be detached at that time.
When an object is detached it is typically passed to a different layer of an application and potentially
changed. During the course of the operation of the system it may be required to know what is loaded
in the object and what is dirty (has been changed since detaching). DataNucleus provides an extension
to allow interrogation of the detached object.
So you have access to the names of the fields that were loaded when detaching the object, and also to
the names of the fields that have been updated since detaching.
When serialization occurs on a Detachable object, the jdoDetachedState field is written to the
serialized object stream. On deserialize, this field is written back to the new deserialized instance.
This process occurs transparently to the application. However, if deserialization occurs with an un-
enhanced version of the class, the detached state is lost.
Serialization and deserialization of Detachable classes and un-enhanced versions of the same class
is only possible if the field serialVersionUID is added. It's recommended during development of
the class, to define the serialVersionUID and make the class to implement the java.io.Serializable
interface, as the following example:
91 Datastore Connection
.......................................................................................................................................
• PMF : single connection at any one time for datastore-based value generation. Obtained just for
the operation, then released
• PMF : single connection at any one time for schema-generation. Obtained just for the operation,
then released
• PM : single connection at any one time. When in a transaction the connection is held from
the point of retrieval until the transaction commits or rolls back; the exact point at which the
connection is obtained is defined more fully below. When used for non-transactional operations
the connection is obtained just for the specific operation (unless configured to retain it).
If you have multiple threads using the same PersistenceManager then you can get "ConnectionInUse"
problems where another operation on another thread comes in and tries to perform something while
that first operation is still in use. This happens because the JDO spec requires an implementation to
use a single datastore connection at any one time. When this situation crops up the user ought to use
multiple PersistenceManagers.
Another important aspect is use of queries for Optimistic transactions, or for non-transactional
contexts. In these situations it isn't possible to keep the datastore connection open indefinitely and so
when the Query is executed the ResultSet is then read into core making the queried objects available
thereafter.
For the datastores supported by DataNucleus, the "native" object is of the following types
• RDBMS : java.sql.Connection
• Excel : org.apache.poi.hssf.usermodel.HSSFWorkbook
• OOXML : org.apache.poi.hssf.usermodel.XSSFWorkbook
• ODF : org.odftoolkit.odfdom.doc.OdfDocument
• LDAP : javax.naming.ldap.LdapContext
• MongoDB : com.mongodb.DB
• HBase : NOT SUPPORTED
• JSON : NOT SUPPORTED
• XML : org.w3c.dom.Document
• NeoDatis : org.neodatis.odb.ODB
• GAE Datastore : com.google.appengine.api.datastore.DatastoreService
• Neo4j : org.neo4j.graphdb.GraphDatabaseService
• Cassandra : com.datastax.driver.core.Session
The "JDOConnection"
in the case of DataNucleus is a wrapper to the native connection for the type of datastore being used.
You now have a connection allowing direct access to the datastore. Things to bear in mind with this
connection
• You must return the connection back to the PersistenceManager before performing any JDO PM
operation. You do this by calling conn.close()
• If you don't return the connection and try to perform a JDO PM operation which requires the
connection then a JDOUserException is thrown.
When you create a PersistenceManagerFactory using a connection URL, driver name, and the
username/password, this does not necessarily pool the connections (so they would be efficiently
opened/closed when needed to utilise datastore resources in an optimum way). For some of the
supported datastores DataNucleus allows you to utilise a connection pool to efficiently manage the
connections to the datastore when specifying the datastore via the URL. We currently provide support
for the following
• RDBMS : DBCP we allow use of externally-defined DBCP, but also provide a builtin DBCP
v1.4
• RDBMS : C3P0
• RDBMS : Proxool
• RDBMS : BoneCP
• RDBMS : HikariCP
• RDBMS : Tomcat
• RDBMS : Manually creating a DataSource for a 3rd party software package
• RDBMS : Custom Connection Pooling Plugins for RDBMS using the DataNucleus
ConnectionPoolFactory interface
• RDBMS : Using JNDI, and lookup a connection DataSource.
• LDAP : Using JNDI
You need to specify the persistence property datanucleus.connectionPoolingType to be whichever
of the external pooling libraries you wish to use (or "None" if you explicitly want no pooling).
DataNucleus provides two sets of connections to the datastore - one for transactional usage, and
one for non-transactional usage. If you want to define a different pooling for nontransactional usage
then you can also specify the persistence property datanucleus.connectionPoolingType.nontx to
whichever is required.
datanucleus.connectionPool.driver.defaultRowPrefetch=50
and it will pass in defaultRowPrefetch as "50" into the driver used by the connection pool.
So the PMF will use connection pooling using DBCP. To do this you will need commons-dbcp,
commons-pool and commons-collections JARs to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for DBCP are shown below
# Pooling of Connections
datanucleus.connectionPool.maxIdle=10
datanucleus.connectionPool.minIdle=3
datanucleus.connectionPool.maxActive=5
datanucleus.connectionPool.maxWait=60
# Pooling of PreparedStatements
datanucleus.connectionPool.maxStatements=0
datanucleus.connectionPool.testSQL=SELECT 1
datanucleus.connectionPool.timeBetweenEvictionRunsMillis=2400000
datanucleus.connectionPool.minEvictableIdleTimeMillis=18000000
So the PMF will use connection pooling using C3P0. To do this you will need the C3P0 JAR to be in
the CLASSPATH. If you want to configure C3P0 further you can include a "c3p0.properties" in your
CLASSPATH - see the C3P0 documentation for details.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for C3P0 are shown below
# Pooling of Connections
datanucleus.connectionPool.maxPoolSize=5
datanucleus.connectionPool.minPoolSize=3
datanucleus.connectionPool.initialPoolSize=3
# Pooling of PreparedStatements
datanucleus.connectionPool.maxStatements=0
So the PMF will use connection pooling using Proxool. To do this you will need the proxool and
commons-logging JARs to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for Proxool are shown below
datanucleus.connectionPool.maxConnections=10
datanucleus.connectionPool.testSQL=SELECT 1
So the PMF will use connection pooling using BoneCP. To do this you will need the BoneCP JAR
(and SLF4J, google-collections) to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for BoneCP are shown below
# Pooling of Connections
datanucleus.connectionPool.maxPoolSize=5
datanucleus.connectionPool.minPoolSize=3
# Pooling of PreparedStatements
datanucleus.connectionPool.maxStatements=0
So the PMF will use connection pooling using HikariCP. To do this you will need the HikariCP JAR
(and SLF4J, javassist as required) to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for HikariCP are shown below
# Pooling of Connections
datanucleus.connectionPool.maxPoolSize=5
So the PMF will use a DataSource with connection pooling using Tomcat. To do this you will need
the tomcat-jdbc JAR to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling, just like other pools.
91.2.8 RDBMS : Manually create a DataSource (e.g DBCP, C3P0, Proxool, etc)
We could have used the built-in DBCP support which internally creates a DataSource
ConnectionFactory, alternatively the support for external DBCP, C3P0, Proxool, BoneCP etc,
however we can also do this manually if we so wish. Let's demonstrate how to do this with one of the
most used pools Apache Commons DBCP
With DBCP you need to generate a javax.sql.DataSource, which you will then pass to DataNucleus.
You do this as follows
Note that we haven't passed the dbUser and dbPassword to the PMF since we no longer need to
specify them - they are defined for the pool so we let it do the work. As you also see, we set the data
source for the PMF. Thereafter we can sit back and enjoy the performance benefits. Please refer to the
documentation for DBCP for details of its configurability (you will need commons-dbcp, commons-
pool, and commons-collections in your CLASSPATH to use this above example).
Once you have turned connection pooling on if you want more control over the pooling you can also
set the following persistence properties
• datanucleus.connectionPool.maxPoolSize : max size of pool
• datanucleus.connectionPool.initialPoolSize : initial size of pool
DataNucleus allows use of a data source that represents the datastore in use. This is often just a URL
defining the location of the datastore, but there are in fact several ways of specifying this data source
depending on the environment in which you are running.
• Nonmanaged Context - Java Client
• Managed Context - Servlet
• Managed Context - JEE
equally be a servlet engine (e.g Tomcat, Jetty). Here we are in a non-managed context, and we use the
following properties when creating our PersistenceManagerFactory, and refer to the JNDI data source
of the server.
If the data source is avaiable in WebLogic, the simplest way of using a data source outside the
application server is as follows.
If the data source is avaiable in Websphere, the simplest way of using a data source outside the
application server is as follows.
<parameter>
<name>url</name>
<value>jdbc:mysql://127.0.0.1:3306/datanucleus?autoReconnect=true</value>
</parameter>
<parameter>
<name>driverClassName</name>
<value>com.mysql.jdbc.Driver</value>
</parameter>
<parameter>
<name>username</name>
<value>mysql</value>
</parameter>
<parameter>
<name>password</name>
<value></value>
</parameter>
</ResourceParams>
</Context>
With this Tomcat JNDI data source we would then specify the PMF ConnectionFactoryName as
java:comp/env/jdbc/datanucleus.
92 Transactions
.......................................................................................................................................
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
pm.close();
The basic idea with Locally-Managed transactions is that you are managing the transaction start and
end.
UserTransaction ut = (UserTransaction)
new InitialContext().lookup("java:comp/UserTransaction");
PersistenceManager pm = pmf.getPersistenceManager();
try
{
ut.begin();
ut.commit();
}
finally
{
pm.close();
}
UserTransaction ut = (UserTransaction)
new InitialContext().lookup("java:comp/UserTransaction");
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin(); // Starts the UserTransaction
Important : please note that you need to set both transactional and nontransactional datasources, and
the nontransactional cannot be JTA.
example except that you would OMIT the tx.begin(), tx.commit(), tx.rollback() since the JEE
container will be doing this for you.
92.1.5 No Transactions
DataNucleus allows the ability to operate without transactions. With JDO this is enabled by default
(see the 2 properties datanucleus.NontransactionalRead, datanucleus.NontransactionalWrite
set to true). This means that you can read objects and make updates outside of transactions. This is
effectively "auto-commit" mode.
PersistenceManager pm = pmf.getPersistenceManager();
pm.close();
When using non-transactional operations, you need to pay attention to the persistence property
datanucleus.nontx.atomic. If this is true then any persist/delete/update will be committed to the
datastore immediately. If this is false then any persist/delete/update will be queued up until the next
transaction (or pm.close()) and committed with that.
92.1.6 Flushing
During a transaction, depending on the configuration, operations don't necessarily go to the datastore
immediately, often waiting until commit. In some situations you need persists/updates/deletes to be in
the datastore so that subsequent operations can be performed that rely on those being handled first. In
this case you can flush all outstanding changes to the datastore using
pm.flush();
A convenient vendor extension is to find which objects are waiting to be flushed at any time, like this
List<ObjectProvider> objs =
((JDOPersistenceManager)pm).getExecutionContext().getObjectsToBeFlushed();
• read-uncommitted : dirty reads, non-repeatable reads and phantom reads can occur
• read-committed : dirty reads are prevented; non-repeatable reads and phantom reads can occur
• repeatable-read : dirty reads and non-repeatable reads are prevented; phantom reads can occur
• serializable : dirty reads, non-repeatable reads and phantom reads are prevented
The default (in DataNucleus) is read-committed. An attempt to set the isolation level to an
unsupported value (for the datastore) will throw a JDOUserException. As an alternative you can also
specify it on a per-transaction basis as follows (using the names above).
Transaction tx = pm.currentTransaction();
...
tx.setIsolationLevel("read-committed");
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.setSynchronization(new javax.transaction.Synchronization()
{
public void beforeCompletion()
{
// before commit or rollback
}
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
pm.close();
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.setRollbackOnly();
tx.rollback();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
pm.close();
Any call to commit on the transaction will throw an exception forcing the user to roll it back.
Transaction tx = pm.currentTransaction();
tx.setSerializeRead(true);
Query q = pm.newQuery(...);
q.setSerializeRead(true);
With pessimistic locking DataNucleus will grab a datastore connection at the first operation, and
maintain it for the duration of the transaction. A single connection is used for the transaction (with the
exception of any Identity Generation operations which need datastore access, so these can use their
own connection).
In terms of the process of pessimistic (datastore) locking, we demonstrate this below.
not be updated by other transactions during the duration of this transaction, so the changes are not
propagated to the datastore until commit()/flush(). The data is checked just before commit to ensure
the integrity in this respect. The most convenient way of checking data for updates is to maintain a
column on each table that handles optimistic locking data. The user will decide this when generating
their MetaData.
Rather than placing version/timestamp columns on all user datastore tables, JDO2 allows the user
to notate particular classes as requiring optimistic treatment. This is performed by specifying in
MetaData or annotations the details of the field/column to use for storing the version - see versioning
for JDO. With JDO the version is added in a surrogate column, whereas a vendor extension allows
you to have a field in your class ready to store the version.
In JDO2 the version is stored in a surrogate column in the datastore so it also provides a method for
accessing the version of an object. You can call JDOHelper.getVersion(object) and this returns the
version as an Object (typically Long or Timestamp). This will return null for a transient object, and
will return the version for a persistent object. If the object is not persistable then it will also return
null.
In terms of the process of optimistic locking, we demonstrate this below.
Here no changes make it to the datastore until the user either commits the transaction, or they invoke
flush(). The impact of this is that when performing a query, by default, the results may not contain
the modified objects unless they are flushed to the datastore before invoking the query. Depending
on whether you need the modified objects to be reflected in the results of the query governs what you
do about that. If you invoke flush() just before running the query the query results will include the
changes. The obvious benefit of optimistic locking is that all changes are made in a block and version
checking of objects is performed before application of changes, hence this mode copes better with
external processes updating the objects.
Please note that for some datastores (e.g RDBMS) the version check followed by update/delete is
performed in a single statement.
See also :-
• JDO MetaData reference for <version> element
• JDO Annotations reference for @Version
93 Fetch Groups
.......................................................................................................................................
<class name="MyClass">
...
<field name="fieldX" default-fetch-group="true"/>
</class>
or using annotations
@Persistent(defaultFetchGroup="true")
SomeType fieldX;
When a PersistenceManager is created it starts with a FetchPlan of the "default" fetch group. That is,
if we call
Collection fetchGroups = fp.getGroups();
this will have one group, called "default". At runtime, if you have been using other fetch groups and
want to revert back to the default fetch group at any time you simply do
fp.setGroup(FetchPlan.DEFAULT);
class MyClass
{
String name;
HashSet coll;
MyOtherClass other;
}
and we want to have the other field loaded whenever we load objects of this class, we define our
MetaData as
<package name="mydomain">
<class name="MyClass">
<field name="name">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="coll" persistence-modifier="persistent">
<collection element-type="mydomain.Address"/>
<join/>
</field>
<field name="other" persistence-modifier="persistent"/>
<fetch-group name="otherfield">
<field name="other"/>
</fetch-group>
</class>
</package>
or using annotations
@PersistenceCapable
@FetchGroup(name="otherfield", members={@Persistent(name="other")})
public class MyClass
{
...
}
So we have defined a fetch group called "otherfield" that just includes the field with name other. We
can then use this at runtime in our persistence code.
PersistenceManager pm = pmf.getPersistenceManager();
pm.getFetchPlan().addGroup("otherfield");
By default the FetchPlan will include the default fetch group. We have changed this above by adding
the fetch group "otherfield", so when we retrieve an object using this PersistenceManager we will be
retrieving the fields name AND other since they are both in the current FetchPlan. We can take the
above much further than what is shown by defining nested fetch groups in the MetaData. In addition
we can change the FetchPlan just before any PersistenceManager operation to control what is fetched
during that operation. The user has full flexibility to add many groups to the current Fetch Plan. This
gives much power and control over what will be loaded and when. A big improvement over JDO 1.0
The FetchPlan applies not just to calls to PersistenceManager.getObjectById(), but also to
PersistenceManager.newQuery(), PersistenceManager.getExtent(), PersistenceManager.detachCopy
and much more besides.
To read more about named fetch-groups and how to use it with attach/detach you can look at our
Tutorial on DAO Layer design.
import org.datanucleus.FetchGroup;
So we use the DataNucleus PMF as a way of creating a FetchGroup, and then register that
FetchGroup with the PMF for use by all PMs. We then enable our FetchGroup for use in the
FetchPlan by using its group name (as we do for a static group). The FetchGroup allows you to add/
remove the fields necessary so you have full API control over the fields to be fetched.
and we want to detach field1 of instances of MyClass1, down 2 levels - so detaching the initial
"field1" MyClass2 object, and its "field2" MyClass3 instance. So we define our fetch-groups like this
<class name="MyClass1">
...
<fetch-group name="includingField1">
<field name="field1"/>
</fetch-group>
</class>
<class name="MyClass2">
...
<fetch-group name="includingField2">
<field name="field2"/>
</fetch-group>
</class>
A further refinement to this global fetch depth setting is to control the fetching of recursive fields.
This is performed via a MetaData setting "recursion-depth". A value of 1 means that only 1 level of
objects will be fetched. A value of -1 means there is no limit on the amount of recursion. The default
is 1. Let's take an example
<class name="Directory">
<field name="children">
<collection element-type="Directory"/>
</field>
<fetch-group name="grandchildren">
<field name="children" recursion-depth="2"/>
</fetch-group>
...
</class>
So when we fetch a Directory, it will fetch 2 levels of the children field, hence fetching the children
and the grandchildren.
94 Query API
.......................................................................................................................................
We recommend using JDOQL for queries wherever possible since it is object-based and datastore agnostic,
giving you extra flexibility in the future. If not possible using JDOQL, only then use a language appropriate to
the datastore in question
Query q = pm.newQuery("javax.jdo.query.JDOQL",
"SELECT FROM mydomain.MyClass WHERE field2 < threshold " +
"PARAMETERS java.util.Date threshold");
or alternatively
Query q = pm.newQuery(MyClass.class);
q.setFilter("field2 < threshold");
q.declareParameters("java.util.Date threshold");
TypesafeQuery<MyClass> q = pm.newTypesafeQuery(MyClass.class);
QMyClass cand = QMyClass.candidate();
List<Product> results =
q.filter(cand.field2.lt(q.doubleParameter("threshold"))).executeList();
Please note that with the query API you can also specify execution time information for the query,
such as whether it executes in memory, or whether to apply a datastore timeout etc.
q.compile();
By default, when a query is executed, it will execute in the datastore with what is present in the
datastore at that time. If there are outstanding changes waiting to be flushed then these will not feature
in the results. To flush these changes before execution, set the following query "extension" before
calling execute
q.addExtension("datanucleus.query.flushBeforeExecution","true");
<jdo>
<package name="mydomain">
<class name="Product">
...
<query name="SoldOut" language="javax.jdo.query.JDOQL"><![CDATA[
SELECT FROM mydomain.Product WHERE status == "Sold Out"
]]></query>
</class>
</package>
</jdo>
So we have a JDOQL query called "SoldOut" defined for the class Product that returns all Products
(and subclasses) that have a status of "Sold Out". Out of interest, what we would then do in our
application to execute this query woule be
Query q = pm.newNamedQuery(mydomain.Product.class,"SoldOut");
Collection results = (Collection)q.execute();
The above example was for the JDOQL object-based query language. We can do a similar thing using
SQL, so we define the following in our MetaData for our Product class
<jdo>
<package name="mydomain">
<class name="Product">
...
<query name="PriceBelowValue" language="javax.jdo.query.SQL"><![CDATA[
SELECT NAME FROM PRODUCT WHERE PRICE < ?
]]></query>
</class>
</package>
</jdo>
So here we have an SQL query that will return the names of all Products that have a price less than
a specified value. This leaves us the flexibility to specify the value at runtime. So here we run our
named query, asking for the names of all Products with price below 20 euros.
Query q = pm.newNamedQuery(mydomain.Product.class,"PriceBelowValue");
Collection results = (Collection)q.execute(20.0);
All of the examples above have been specifed within the <class> element of the MetaData. You can,
however, specify queries below <jdo> in which case the query is not scoped by a particular candidate
class. In this case you must put your queries in any of the following MetaData files
/META-INF/package.jdo
/WEB-INF/package.jdo
/package.jdo
/META-INF/package-{mapping}.orm
/WEB-INF/package-{mapping}.orm
/package-{mapping}.orm
/META-INF/package.jdoquery
/WEB-INF/package.jdoquery
/package.jdoquery
Query q = pm.newQuery(...);
q.getFetchPlan().setFetchSize(FetchPlan.FETCH_SIZE_OPTIMAL);
• FETCH_SIZE_GREEDY - DataNucleus will read all objects in at query execution. This can be
efficient for queries with few results, and very inefficient for queries returning large result sets.
• A positive value - DataNucleus will read this number of objects at query execution. Thereafter it
will read the objects when requested.
In addition to the number of objects fetched, you can also control which fields are fetched for each
object of the candidate type. This is controlled via the FetchPlan. For RDBMS any single-valued
member will be fetched in the original SQL query, but with multiple-valued members this is not
supported. However what will happen is that any collection field will be retrieved in a single SQL
query for all candidate objects; this avoids the "N+1" problem, resulting in 1 original SQL query
plus 1 SQL query per collection member. Note that you can disable this by either not putting multi-
valued fields in the FetchPlan, or by setting the query extension "datanucleus.multivaluedFetch"
to "none" (default is "bulk-fetch" using the single SQL per field). For non-RDBMS datastores the
collection/map is stored by way of a Collection of ids of the related objects in a single "column" of the
object and so is retrievable in the same query. See also Fetch Groups.
DataNucleus also allows an extension to give further control. As mentioned above, when the
transaction containing the Query is committed, all remaining results are read so that they can then be
accessed later (meaning that the query is still usable). Where you have a large result set and you don't
want this behaviour you can turn it off by specifying a Query extension
q.addExtension("datanucleus.query.loadResultsAtCommit", "false");
so when the transaction is committed, no more results will be available from the query.
In some situations you don't want all FetchPlan fields retrieving, and DataNucleus provides an
extension to turn this off, like this
q.addExtension("datanucleus.query.useFetchPlan", "false");
Query q = pm.newQuery(...);
q.setSerializeRead(true);
You can also specify this for all queries for all PMs using a PMF property
datanucleus.SerializeRead. In addition you can perform this on a per-transaction basis by doing
tx.setSerializeRead(true);
If the datastore in use doesn't support locking of objects then this will do nothing
When using optimistic transactions all updates to data are held until flush()/commit(). This means
that executing a query may not take into account changes made during that transaction in some
objects. DataNucleus allows a convenience of calling flush() just before execution of queries so that
all updates are taken into account. The property name is datanucleus.query.flushBeforeExecution
and defaults to "false".
To do this on a per query basis for JDO you would do
query.addExtension("datanucleus.query.flushBeforeExecution","true");
You can also specify this for all queries using a persistence property
datanucleus.query.flushBeforeExecution which would then apply to ALL queries for that PMF.
q.setDatastoreReadTimeout(1000);
Sets the timeout for this query (in milliseconds). Will throw a JDOUnsupportedOperationException if
the query implementation doesn't support timeouts.
q.setDatastoreWriteTimeout(1000);
Sets the timeout for this query (in milliseconds) when it is a delete/update. Will throw a
JDOUnsupportedOperationException if the query implementation doesn't support timeouts.
95 Query Cache
.......................................................................................................................................
JDO doesn't currently define a mechanism for caching of queries. DataNucleus provides 3 levels of
caching
query.addExtension("datanucleus.query.compilation.cached", "true");
query.addExtension("datanucleus.query.compilation.cached", "true");
query.addExtension("datanucleus.query.results.cached", "true");
query.addExtension("datanucleus.query.resultCache.validateObjects", "false");
Obviously with a cache of query results, you don't necessarily want to retain this cached over a long
period. In this situation you can evict results from the cache like this.
import org.datanucleus.api.jdo.JDOQueryCache;
import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
...
JDOQueryCache cache = ((JDOPersistenceManagerFactory)pmf).getQueryCache();
cache.evict(query);
which evicts the results of the specific query. The JDOQueryCache has more options available should
you need them ...
96 JDOQL
.......................................................................................................................................
Single-String JDOQL :
Query q = pm.newQuery(
"SELECT FROM mydomain.Person WHERE lastName == 'Jones' && " +
"age < age_limit PARAMETERS int age_limit");
List<Person> results = (List<Person>)q.execute(20);
Declarative JDOQL :
Query q = pm.newQuery(Person.class);
q.setFilter("lastName == 'Jones' && age < age_limit");
q.declareParameters("int age_limit");
List<Person> results = (List<Person>)q.execute(20);
So here in our example we select all "Person" objects with surname of "Jones" and where the persons
age is below 20. The language is intuitive for Java developers, and is intended as their interface to
accessing the persisted data model. As can be seen above, the query is made up of distinct parts. The
class being selected (the SELECT clause in SQL), the filter (which equates to the WHERE clause in
SQL), together with any sorting (the ORDER BY clause in SQL), etc.
In this section we will express all examples using the single-string format since it is the simplest
to highlight how to use JDOQL, so please refer to the Declarative JDOQL and Typesafe JDOQL
guides for details if wanting to use those.
The "keywords" in the query are shown in UPPER CASE but can be in UPPER or lower case (but not
MiXeD case). So giving an example
SELECT UNIQUE FROM mydomain.Employee ORDER BY departmentNumber
DataNucleus also allows you to specify a candidate class as persistent interface. This is used where
we want to query for instances of implementations of the interface. Let's take an example. We have an
interface
@PersistenceCapable
public interface ComputerPeripheral
{
@PrimaryKey
long getId();
void setId(long val);
@Persistent
String getManufacturer();
void setManufacturer(String name);
@Persistent
String getModel();
void setModel(String name);
}
@PersistenceCapable
public class Mouse implements ComputerPeripheral
{
...
}
@PersistenceCapable
public class Keyboard implements ComputerPeripheral
{
...
}
So we have made our interface persistable, and defined the identity property(ies) there. The
implementations of the interface will use the identity defined in the interface. To query it we simply
do
96.1.3 Filter
The most important thing to remember when defining the filter for JDOQL is that think how you
would write it in Java, and its likely the same. The filter has to be a boolean expression, and can
include the candidate, fields/properties, literals, methods, parameters, variables, operators,
instanceof, subqueries and casts.
96.1.4 Fields/Properties
In JDOQL you refer to fields/properties in the query by referring to the field/bean name. For example,
if you are querying a candidate class called Product and it has a field "price", then you access it like
this
price < 150.0
Note that, just like in Java, if you want to refer to a field/property of the candidate you can prefix the
field by this
this.price < 150.0
You can also chain field references if you have a candidate class Product with a field of (persistable)
type Inventory, which has a field name, so you could do
this.inventory.name == 'Backup'
In addition to the persistent fields, you can also access "public static final" fields of any class. You can
do this as follows
taxPercent < mydomain.Product.TAX_BAND_A
So this will find all products that include a tax percentage less than some "BAND A" level. Where
you are using "public static final" fields you can either fully-qualify the class name or you can include
it in the "imports" section of the query (see later).
An important thing to remember with JDOQL is that you do not do explicit joins. You instead
use the fields/properties and navigate to the object you want to make use of in your query.
With 1-1/N-1 relations this is simply a reference to the field/property, and place some restriction on it,
like this
this.inventory.name == 'MyInventory'
and thereafter refer to elemVar for the element in the collection to place restrictions on the element.
Similarly you can use elemVar in the result clause
96.1.5 Methods
When writing the "filter" for a JDOQL Query you can make use of some methods on the various Java
types. The range of methods included as standard in JDOQL is not as flexible as with the true Java
types, but the ones that are available are typically of much use. While DataNucleus supports all of the
methods in the JDO standard, it also supports several yet to be standardised (extension) method. The
tables below also mark whether a particular method is supported for evaluation in-memory.
Please note that you can easily add support for other methods for evaluation "in-memory" using this
DataNucleus plugin point
Please note that you can easily add support for other methods with RDBMS datastore using this
DataNucleus plugin point
Here's an example using a Product class, looking for objects which their abbreviation is the beginning
of a trade name. The trade name is provided as parameter.
Declarative JDOQL :
Query query = pm.newQuery(mydomain.Product.class);
query.setFilter(":tradeName.startsWith(this.abbreviation)");
List results = (List)query.execute("Workbook Advanced");
Single-String JDOQL :
Query query = pm.newQuery(
"SELECT FROM mydomain.Product " +
"WHERE :tradeName.startsWith(this.abbreviation)");
List results = (List)query.execute("Workbook Advanced");
Here's an example demonstrating use of contains(). We have an Inventory class that has a Collection
of Product objects, and we want to find the Inventory objects with 2 particular Products in it. Here we
make use of a variable ( prd to represent the Product being contained
Declarative JDOQL :
Query query = pm.newQuery(mydomain.Inventory.class);
query.setFilter("products.contains(prd) && (prd.name=='product 1' || prd.name=='product 2')");
List results = (List)query.execute();
Single-String JDOQL:
Query query = pm.newQuery(
"SELECT FROM mydomain.Inventory " +
"WHERE products.contains(prd) && (prd.name=='product 1' || prd.name=='product 2')");
List results = (List)query.execute();
Here's an example using a Product class as a value in a Map. Our example represents an organisation
that has several Inventories of products. Each Inventory of products is stored using a Map, keyed
by the Product name. The query searches for all Inventories that contain a product with the name
"product 1".
Declarative JDOQL :
Query query = pm.newQuery(mydomain.Inventory.class, "products.containsKey('product 1')");
List results = (List)query.execute();
Single-String JDOQL :
Query query = pm.newQuery(
"SELECT FROM mydomain.Inventory " +
"WHERE products.containsKey('product 1')");
List results = (List)query.execute();
class Inventory
{
Map<String, Product> products;
...
}
class Product
{
...
}
96.1.6 Literals
JDOQL supports literals of the following types : Number, boolean, character, String, and null. When
String literals are specified using single-string format they should be surrounded by single-quotes '.
96.1.7 Parameters
With a query you can pass values into the query as parameters. This is useful where you don't want to
embed particular values in the query itself, so making it reusable with different values. JDOQL allows
two types of parameters.
In some situations you may have a map of parameters keyed by their name, yet the query in question
doesn't need all parameters. Normal JDO execution would throw an exception here since they are
inconsistent with the query. You can omit this check by setting
q.addExtension("datanucleus.query.ignoreParameterCountCheck", "true");
96.1.8 Variables
In JDOQL you can connect two parts of a query using something known as a variable. For example,
we want to retrieve all objects with a collection that contains a particular element, and where the
element has a particular field value. We define a query like this
So we have a variable in our query called prod that connects the two parts. You can declare your
variables (using the VARIABLES keyword in the single-string form, or via the declareVariables
method) if you want to define the type like here ( explicit), or you can leave them for the query
compilation to determine ( implicit).
Note that if declaring multiple variables then they should be semicolon-separated.
96.1.9 Imports
JDOQL uses the imports declaration to create a type namespace for the query. During query
compilation, the classes used in the query, if not fully qualified, are searched in this namespace. The
type namespace is built with primitives types, java.lang.* package, package of the candidate class,
import declarations (if any).
To resolve a class, the JDOQL compiler will use the class fully qualified name to load it, but if the
class is not fully qualified, it will search by prefixing the class name with the imported package
names declared in the type namespace. All classes loaded by the query must be acessible by either the
candidate class classloader, the PersistenceManager classloader or the current Thread classloader. The
search algorithm for a class in the JDOQL compiler is the following:
SELECT p.personNum, IF (p.age < 18) 'Youth' ELSE IF (p.age >= 18 && p.age < 65) 'Adult' ELSE 'Old' FROM m
So in this case the second result value will be a String, either "Youth", "Adult" or "Old" depending
on the age of the person. Note that this is not in any JDO spec yet, but should be soon. The BNF
structure of the JDOQL IF ELSE expression is
96.1.11 Operators
The following list describes the operator precedence in JDOQL.
1. Cast
2. Unary ("~") ("!")
3. Unary ("+") ("-")
4. Multiplicative ("*") ("/") ("%")
5. Additive ("+") ("-")
6. Relational (">=") (">") ("<=") ("<") ("instanceof")
7. Equality ("==") ("!=")
8. Boolean logical AND ("&")
9. Boolean logical OR ("|")
10.Conditional AND ("&&")
11.Conditional OR ("||")
The concatenation operator(+) concatenates a String to either another String or Number.
Concatenations of String or Numbers to null results in null.
96.1.12 instanceof
JDOQL allows the Java keyword instanceof so you can compare objects against a class.
Let's take an example. We have a class A that has a field "b" of type B and B has subclasses B1, B2,
B3. Clearly the field "b" of A can be of type B, B1, B2, B3 etc, and we want to find all objects of type
A that have the field "b" that is of type B2. We do it like this
Declarative JDOQL :
Query query = pm.newQuery(A.class);
query.setFilter("b instanceof mydomain.B2");
List results = (List)query.execute();
Single-String JDOQL :
Query query = pm.newQuery("SELECT FROM mydomain.A WHERE b instanceof mydomain.B2");
List results = (List)query.execute();
96.1.13 casting
JDOQL allows use of Java-style casting so you can type-convert fields etc.
Let's take an example. We have a class A that has a field "b" of type B and B has subclasses B1, B2,
B3. The B2 subtype has a field "other", and we know that the filtered A will have a B2. You could
specify a filter using the "B2.other" field like this
((mydomain.B2)b).other == :someVal"
96.1.14 Subqueries
With JDOQL the user has a very flexible query syntax which allows for querying of the vast majority
of data components in a single query. In some situations it is desirable for the query to utilise the
results of a separate query in its calculations. JDO allows subqueries, so that both calculations can be
performed in one query. Here's an example, using single-string JDOQL
So we want to find all Employees that have a salary greater than the average salary. In single-string
JDOQL the subquery must be in parentheses (brackets). Note that we have defined the subquery with
an alias of "e", whereas in the outer query the alias is "this".
We can specify the same query using the Declarative API, like this
So we define a subquery as its own Query (that could be executed just like any query if so desired),
and the in the main query have an implicit variable that we define as being represented by the
subquery.
So with single-string JDOQL we make use of the alias identifier "this" to link back to the outer query.
Using the Declarative API, to achieve the same thing we would do
So with the Declarative API we make use of parameters, and the last argument to addSubquery is the
value of the parameter lastNameParam.
Query q = pm.newQuery(Employee.class);
q.setFilter("this.weeklyhours > averageWeeklyhours");
q.addSubquery(averageHoursQuery, "double averageWeeklyhours", "this.department.employees", null);
so now our subquery has a candidate related to the outer query candidate.
There are situations when you want to return a single number for a column, representing an aggregate
of the values of all records. There are 5 standard JDO aggregate functions available. These are
• avg(val) - returns the average of "val". "val" can be a field, numeric field expression or "distinct
field". Returns double.
• sum(val) - returns the sum of "val". "val" can be a field, numeric field expression, or "distinct
field". Returns the same type as the type being summed
• count(val) - returns the count of records of "val". "val" can be a field, or can be "this", or
"distinct field". Returns long
• min(val) - returns the minimum of "val". "val" can be a field. Returns the same type as the type
used in "min"
• max(val) - returns the maximum of "val". "val" can be a field. Returns the same type as the type
used in "max"
So to utilise these you could specify a result like
max(price), min(price)
This will return a single row of results with 2 values, the maximum price and the minimum price.
Note that what you specify in the result defines what form of result you get back when executing the
query.
• {ResultClass} - this is returned if you have only a single row in the results and you specified a
result class.
• Object - this is returned if you have only a single row in the results and a single column. This is
achived when you specified either UNIQUE, or just an aggregate (e.g "max(field2)")
• Object[] - this is returned if you have only a single row in the results, but more than 1 column
(e.g "max(field1), avg(field2)")
• List<{ResultClass}> - this is returned if you specified a result class.
• List<Object> - this is returned if you have only a single column in the result, and you don't have
only aggregates in the result (e.g "field2")
• List<Object[]> - this is returned if you have more than 1 column in the result, and you don't
have only aggregates in the result (e.g "field2, avg(field3)")
...
}
• Default constructor, and setters for the different result columns, using the alias name for each
column as the property name of the setter. For example
public Price()
{
}
...
}
which will sort primarily by field1 in ascending order, then secondarily by field2 in descending order.
Although it is not (yet) standard JDOQL, DataNucleus also supports specifying a directive for where
NULL values of the ordered field/property go in the order, so the full syntax supported is
fieldName [ASC|DESC] [NULLS FIRST|NULLS LAST]
Note that this is only supported for a few RDBMS (H2, HSQLDB, PostgreSQL, DB2, Oracle, Derby,
Firebird, SQLServer v11+).
which will return just the results numbers 10-19 inclusive. Obviously bear in mind that if specifying
the range then you should really specify an ordering otherwise the range positions will be not
defined.
The typical use of a JDOQL query is to translate it into the native query language of the datastore
and return objects matched by the query. Sometimes you want to query over a set of objects that you
have to hand, or for some datastores it is simply impossible to support the full JDOQL syntax in the
datastore native query language. In these situation we need to evaluate the query in-memory. In the
latter case of the datastore not supported the full JDOQL syntax we evaluate as much as we can in the
datastore and then instantiate those objects and evaluate further in-memory. Here we document the
current capabilities of in-memory evaluation in DataNucleus.
To enable evaluation in memory you specify the query extension
datanucleus.query.evaluateInMemory to true as follows
query.addExtension("datanucleus.query.evaluateInMemory","true");
This is also useful where you have a Collection of (persisted) objects and want to run a query over the
Collection. Simply turn on in-memory evaluation, and supply the candidate collection to the query,
and no communication with the datastore will be needed.
The value returned is the number of instances that were deleted. Note that this will perform any
cascade deletes that are defined for these instances. In addition, all instances in memory will reflect
this deletion.
DataNucleus provides an extension to allow bulk deletion. This differs from the "Deletion by Query"
above in that it simply goes straight to the datastore and performs a bulk delete, leaving it to the
datastore referential integrity to handle relationships. To enable "bulk delete" you need the persistence
property datanucleus.query.jdoql.allowAll set to true. You then perform "bulk delete" like this
DataNucleus provides an extension to allow bulk update. This allows you to do bulk updates direct to
the datastore without having to load objects into memory etc. To enable "bulk update" you need the
persistence property datanucleus.query.jdoql.allowAll set to true. You then perform "bulk update"
like this
Query query = pm.newQuery("UPDATE mydomain.A SET this.value=this.value-5.0 WHERE this.value > 100");
Long number = (Long)query.execute();
97 JDOQL Declarative
.......................................................................................................................................
In this Query, we create it to return objects of type mydomain.MyClass (or subclasses), and set the
filter to restrict to instances of that type which have the field field2 less than some threshold value,
which we don't know at that point. We've specified the query like this because we want to pass
the threshold value in dynamically. We then import the type of our threshold parameter, and the
parameter itself, and set the ordering of the results from the Query to be in ascending order of some
field field1. The Query is then executed, passing in the threshold value. The example is to highlight
the typical methods specified for a Query. Clearly you may only specify the Query line if you wanted
something very simple. The result of the Query is cast to a List since in this case it returns a List of
results.
97.1.1 setClass()
Set the class of the candidate instances of the query. The class specifies the class of the candidates of
the query. Elements of the candidate collection that are of the specified class are filtered before being
put into the results.
97.1.2 setUnique()
Specify that only the first result of the query should be returned, rather than a collection. The execute
method will return null if the query result size is 0.
Sometimes you know that the query can only every return 0 or 1 objects. In this case you can simplify
your job by adding
query.setUnique(true);
In this case the return from the execution of the Query will be a single Object, so you've no need to
use iterators, just cast it to your candidate class type. Note that if you specify unique and there are
more results than just 1 then it will throw a JDOUserException.
97.1.3 setResult()
Specifies what type of data this query should return. If this is unset or set to null, this query returns
instances of the query's candidate class. If set, this query will return expressions, including field
values (projections) and aggregate function results.
The normal behaviour of JDOQL queries is to return a List of Objects of the type of the candidate
class. Sometimes you want to have the query perform some processing and return things like count(),
min(), max() etc. You specify this with
query.setResult("count(param1), max(param2), param3");
In this case the results will be List<Object[]> since there are more than 1 column in each row. If you
have only 1 column in the results then the results would be List<Object>. If you have only aggregates
(sum, avg, min, max, count) in the result clause then there will be only 1 row in the results and so the
results will be of the form Object[] (or Object if only 1 aggregate). Please refer to JDOQL Result
Clauses for more details.
97.1.4 setResultClass()
Specify the type of object in which to return each element of the result of invoking execute(). If the
result is not set or set to null, the result class defaults to the candidate class of the query. If the result
consists of one expression, the result class defaults to the type of that expression. If the result consists
of more than one expression, the result class defaults to Object[].
When you perform a query, using JDOQL or SQL the query will, in general, return a List of objects.
These objects are by default of the same type as the candidate class. This is good for the majority of
situations but there are some situations where you would like to control the output object. This can be
achieved by specifying the Result Class.
query.setResultClass(myResultClass);
• Can be one of Integer, Long, Short, Float, Double, Character, Byte, Boolean, String,
java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.sql.Date, java.sql.Time,
java.sql.Timestamp, or Object[]
• Can be a user defined class, that has either a constructor taking arguments of the same type as
those returned by the query (in the same order), or has a public put(Object, Object) method, or
public setXXX() methods, or public fields.
Where you have a query returning a single field, you could specify the Result Class to be one of the
first group for example. Where your query returns multiple fields then you can set the Result Class to
be your own class. So we could have a query like this
...
}
In this case our query is returning 2 fields (a Double and a String), and these map onto the constructor
arguments, so DataNucleus will create objects of the Price class using that constructor. We could
have provided a class with public fields instead, or provided setXXX methods or a put method. They
all work in the same way.
97.1.5 setRange()
Set the range of results to return. The execution of the query is modified to return only a subset of
results. If the filter would normally return 100 instances, and fromIncl is set to 50, and toExcl is set
to 70, then the first 50 results that would have been returned are skipped, the next 20 results are
returned and the remaining 30 results are ignored. An implementation should execute the query such
that the range algorithm is done at the data store.
Sometimes you have a Query that returns a large number of objects. You may want to just display a
range of these to your user. In this case you can do
query.setRange(10, 20);
This has the effect of only returning items 10 through to 19 (inclusive) of the query's results. The clear
use of this is where you have a web system and you're displaying paginated data, and so the user hits
page down, so you get the next "n" results.
setRange is implemented efficiently for MySQL, Postgresql, HSQL (using the LIMIT SQL keyword)
and Oracle (using the ROWNUM keyword), with the query only finding the objects required by
the user directly in the datastore. For other RDBMS the query will retrieve all objects up to the "to"
record, and will not pass any unnecessary objects that are before the "from" record.
97.1.6 setFilter()
Set the filter for the query. The filter specification is a String containing a Boolean expression that is
to be evaluated for each of the instances in the candidate collection. If the filter is not specified, then
it defaults to "true", which has the effect of filtering the input Collection only for class type.
97.1.7 declareImports()
Set the import statements to be used to identify the fully qualified name of variables or parameters.
Parameters and unbound variables might come from a different class from the candidate class, and
the names need to be declared in an import statement to eliminate ambiguity. Import statements are
specified as a String with semicolon-separated statements.
In JDOQL you can declare parameters and variables. Just like in Java it is often convenient to just
declare a variable as say Date, and then have an import in your Java file importing the java.util.Date
class. The same applies in JDOQL. Where you have defined parameters or variables in shorthand
form, you can specify their imports like this
query.declareVariables("Date startDate");
query.declareParameters("Locale myLocale");
query.declareImports("import java.util.Locale; import java.util.Date;");
Just like in Java, if you declare your parameters or variables in fully-specified form (for example
"java.util.Date myDate") then you do not need any import.
The JDOQL uses the imports declaration to create a type namespace for the query. During query
compilation, the classes used in the query, if not fully qualified, are searched in this namespace. The
type namespace is built with the following:
• primitives types
• java.lang.* package
• package of the candidate class
• import declarations (if any)
To resolve a class, the JDOQL compiler will use the class fully qualified name to load it, but if the
class is not fully qualified, it will search by prefixing the class name with the imported package
names declared in the type namespace. All classes loaded by the query must be acessible by either the
candidate class classloader, the PersistenceManager classloader or the current Thread classloader. The
search algorithm for a class in the JDOQL compiler is the following:
query.declareImports("import java.util.Locale;");
query.declareParameters("Locale myLocale");
or
query.declareParameters("java.util.Locale myLocale");
However, the below example will suffer in performance, due to the search algorithm.
97.1.8 declareParameters()
Declare the list of parameters query execution. The parameter declaration is a String containing
one or more query parameter declarations separated with commas. Each parameter named in the
parameter declaration must be bound to a value when the query is executed.
When using explicit parameters you need to declare them and their types. With the declarative API
you do it like this
query.declareImports("import java.util.*");
query.declareParameters("String myparam1, Date myparam2");
So we make use of imports to define some package names (just like in Java). You can use * notation
too. Note that java.lang is not needed to be imported. Alternatively you could have just done
97.1.9 declareVariables()
Declare the unbound variables to be used in the query. Variables might be used in the filter, and these
variables must be declared with their type. The unbound variable declaration is a String containing
one or more unbound variable declarations separated with semicolons.
With explicit variables, you declare your variables and their types. In declarative JDOQL it is like this
query.declareVariables("mydomain.Product prod");
97.1.10 setOrdering()
Set the ordering specification for the result Collection. The ordering specification is a String
containing one or more ordering declarations separated by commas. Each ordering declaration is the
name of the field on which to order the results followed by one of the following words: "ascending"
or "descending". The field must be declared in the candidate class or must be a navigation expression
starting with a field in the candidate class.
With JDOQL you can specify the ordering using the normal JDOQL syntax for a parameter, and
then add ascending or descending (UPPER or lower case are both valid) are to give the direction. In
addition the abbreviated forms of asc and desc (again, UPPER and lower case forms are accepted) to
save typing. For example, you may set the ordering as follows
query.setOrdering("productId DESC");
97.1.11 setGrouping()
Set the grouping expressions, optionally including a "having" clause. When grouping is specified,
each result expression must either be an expression contained in the grouping, or an aggregate
evaluated once per group.
98 JDOQL Typesafe
.......................................................................................................................................
98.1.1 Preparation
To set up your environment to use this typesafe query API you need to enable annotation processing
(JDK1.7+), place some DataNucleus jars in your build path, and specify an @PersistenceCapable
annotation on your classes to be used in queries (you can still provide the remaining information in
XML metadata if you wish to).
With Maven you need to have the following in your POM
<dependencies>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-api-jdo</artifactId>
<version>(3.9, )</version>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-jdo-query</artifactId>
<version>(3.9, )</version>
</dependency>
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo-api</artifactId>
<version>[3.0, )</version>
</dependency>
...
</dependencies>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
@PersistenceCapable
public class Product
{
@PrimaryKey
long id;
String name;
double value;
...
}
...
}
Note that it has the name Q{className}. Also the generated class, by default, has a public field for
each persistable field/property and is of a type XXXExpression. These expressions allow us to give
Java like syntax when defining your queries (see below). So you access your persistable members in a
query as candidate.name for example.
As mentioned above this is the default style of query class. However you can also create it in
property style, where you access your persistable members as candidate.name() for example.
The benefit of this approach is that if you have 1-1, N-1 relationship fields then it only initialises
the members when called, whereas in the field case above it has to initialise all in the constructor,
so at static initialisation. You enable use of property mode by adding the compiler argument -
AqueryMode=PROPERTY. All examples below use field mode but just add () after the field to see
the equivalent in property mode
pm = pmf.getPersistenceManager();
JDOPersistenceManager jdopm = (JDOPersistenceManager)pm;
TypesafeQuery<Product> tq = jdopm.newTypesafeQuery(Product.class);
QProduct cand = QProduct.candidate();
List<Product> results =
tq.filter(cand.value.lt(40.00).and(cand.name.startsWith("Wal")))
.orderBy(cand.name.asc())
.executeList();
As you see, we create a parametrised query, and then make use of the query class to access the
candidate, and from that make use of its fields, and the various Java methods present for the types of
those fields. Also the API is fluent.
TypesafeQuery<Product> tq = jdopm.newTypesafeQuery(Product.class);
QProduct cand = QProduct.candidate();
List<Object[]> results =
tq.filter(cand.value.lt(40.00).and(cand.name.startsWith(tq.stringParameter("prefix"))))
.orderBy(cand.name.asc())
.setParameter("prefix", "Wal")
.executeResultList(false, cand.name, cand.value);
TypesafeQuery<Product> tq = jdopm.newTypesafeQuery(Product.class);
QProduct cand = QProduct.candidate();
Object[] results = tq.executeResultUnique(false, cand.max(), cand.min());
TypesafeQuery<Product> tq = jdopm.newTypesafeQuery(Product.class);
QProduct cand = QProduct.candidate();
List<Product> results =
tq.filter(cand.value.lt(40.00).and(cand.name.startsWith(tq.stringParameter("prefix"))))
.orderBy(cand.name.asc())
.setParameter("prefix", "Wal")
.executeList();
TypesafeQuery<Inventory> tq = jdopm.newTypesafeQuery(Inventory.class);
QProduct var = QProduct.variable("var");
QInventory cand = QInventory.candidate();
List<Inventory> results =
tq.filter(cand.products.contains(var).and(var.name.startsWith("Wal")))
.executeList();
TypesafeQuery<Product> tq = jdopm.newTypesafeQuery(Product.class);
QProduct cand = QProduct.candidate();
TypesafeSubquery<Product> tqsub = tq.subquery(Product.class, "p");
QProduct candsub = QProduct.candidate("p");
List<Product> results =
tq.filter(cand.value.lt(tqsub.selectUnique(candsub.value.avg())))
.executeList();
TypesafeQuery<Product> tq = jdopm.newTypesafeQuery(Product.class);
QProduct cand = QProduct.candidate();
List<Product> results =
tq.filter(cand.value.lt(40.00)).setCandidates(myCandidates).executeList();
99 SQL
.......................................................................................................................................
You have several forms of SQL queries, depending on what form of output you require.
• No candidate class and no result class - the result will be a List of Objects (when there is a
single column in the query), or a List of Object[]s (when there are multiple columns in the query)
• Candidate class specified, no result class - the result will be a List of candidate class objects, or
will be a single candidate class object (when you have specified "unique"). The columns of the
querys result set are matched up to the fields of the candidate class by name. You need to select a
minimum of the PK columns in the SQL statement.
• No candidate class, result class specified - the result will be a List of result class objects, or
will be a single result class object (when you have specified "unique"). Your result class has to
abide by the rules of JDO2 result classes (see Result Class specification) - this typically means
either providing public fields matching the columns of the result, or providing setters/getters for
the columns of the result.
• Candidate class and result class specified - the result will be a List of result class objects, or
will be a single result class object (when you have specified "unique"). The result class has to
abide by the rules of JDO2 result classes (see Result Class specification).
• Can be one of Integer, Long, Short, Float, Double, Character, Byte, Boolean, String,
java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.sql.Date, java.sql.Time,
java.sql.Timestamp, or Object[]
• Can be a user defined class, that has either a constructor taking arguments of the same type as
those returned by the query (in the same order), or has a public put(Object, Object) method, or
public setXXX() methods, or public fields.
For example, if we are returning two columns like above, an int and a String then we define our result
class like this
...
}
So here we have a result class using the constructor arguments. We could equally have provided a
class with public fields instead, or provided setXXX methods or a put method. They all work in the
same way.
99.1.4 Inserting/Updating/Deleting
In JDO all SQL queries must begin "SELECT ...", and consequently it is not possible to execute
queries that change data. In DataNucleus we have an extension that allows this to be overridden. To
enable this you should pass the property datanucleus.query.sql.allowAll as true when creating the
PersistenceManagerFactory. Thereafter you just invoke your statements like this
Query q = pm.newQuery("javax.jdo.query.SQL",
"UPDATE MY_TABLE SET MY_COLUMN = ? WHERE MY_ID = ?");
you then pass any parameters in as normal for an SQL query. Note that DataNucleus currently
supports queries starting "INSERT", "UPDATE", "MERGE", "DELETE" and "CREATE" as update/
delete queries and so will be invoked with JDBC executeStatementUpdate. If you have a query
starting with something other than that it will be invoked with executeStatementQuery. If your
statement really needs to be executed differently then you should look at contributing support for
those statements to DataNucleus.
99.1.5 Parameters
In JDO SQL queries can have parameters but must be positional. This means that you do as follows
Query q = pm.newQuery("javax.jdo.query.SQL",
"SELECT col1, col2 FROM MYTABLE WHERE col3 = ? AND col4 = ? and col5 = ?");
List results = (List) q.execute(val1, val2, val3);
DataNucleus also supports two further variations. The first is called numbered parameters where we
assign numbers to them, so the previous example could have been written like this
Query q = pm.newQuery("javax.jdo.query.SQL",
"SELECT col1, col2 FROM MYTABLE WHERE col3 = ?1 AND col4 = ?2 and col5 = ?1");
List results = (List) q.execute(val1, val2);
so we can reuse parameters in this variation. The second variation is called named parameters where
we assign names to them, and so the example can be furtehr rewritten like this
Query q = pm.newQuery("javax.jdo.query.SQL",
"SELECT col1, col2 FROM MYTABLE WHERE col3 = :firstVal AND col4 = :secondVal and col5 = :firstVal");
Map params = new HashMap();
params.put("firstVal", val1);
params.put("secondVal", val1);
List results = (List) q.executeWithMap(params);
Here's an example for getting the maximum and miminum of a parameter without a candidate class.
Each row of the results is of the type of our result class. Since our query is for an aggregate, there is
actually only 1 row.
class MyClass
{
String name;
...
}
<jdo>
<package name="org.datanucleus.samples.sql">
<class name="MyClass" identity-type="datastore" table="MYTABLE">
<datastore-identity strategy="identity">
<column name="MY_ID"/>
</datastore-identity>
<field name="name" persistence-modifier="persistent">
<column name="MY_NAME"/>
</field>
</class>
</package>
</jdo>
Query query = pm.newQuery("javax.jdo.query.SQL", "SELECT count(*) FROM PERSON WHERE EMAIL_ADDRESS = ?");
List results = (List) query.execute("[email protected]");
Integer tableSize = (Integer) result.iterator().next();
<jdo>
<package name="org.datanucleus.samples.store">
<class name="Product" identity-type="datastore" table="PRODUCT">
<datastore-identity strategy="identity">
<column name="PRODUCT_ID"/>
</datastore-identity>
<field name="name" persistence-modifier="persistent">
<column name="NAME"/>
</field>
<field name="status" persistence-modifier="persistent">
<column name="STATUS"/>
</field>
Where "sp_who" is the stored procedure being invoked. Clearly the same rules will apply regarding
the results of the stored procedure and mapping them to any result class. The syntax of calling a stored
procedure differs across RDBMS. Some require "CALL ..." and some "EXECUTE ...". Go consult
your manual.
Obviously JDO allows potentially any "query language" to be invoked using its API. With
DataNucleus and RDBMS datastores we can do the following
Now on its own this will simply invoke the define stored procedure ( MY_TEST_SP_1) in the
datastore. Obviously we want more control than that, so this is where you use DataNucleus specifics.
Let's start by accessing the internal stored procedure query
import org.datanucleus.api.jdo.JDOQuery;
import org.datanucleus.store.rdbms.query.StoredProcedureQuery;
...
StoredProcedureQuery spq = (StoredProcedureQuery)((JDOQuery)q).getInternalQuery());
Now we can control things like parameters, and what is returned from the stored procedure query.
Let's start by registering any parameters (IN, OUT, or INOUT) for our stored proc. In our example we
use named parameters, but you can also use positional parameters.
Simple execution is like this (where you omit the paramValueMap if you have no input parameters).
That method returns whether a result set is returned from the stored procedure (some return results,
but some return an update count, and/or output parameters). If we are expecting a result set we then
do
and if we are expecting output parameter values then we get them using the API too. Note again that
you can also access via position rather than name.
That summarises our stored procedure API. It also allows things like multiple result sets for a stored
procedure, all using the StoredProcedureQuery API.
101 JPQL
.......................................................................................................................................
JDO provides a flexible API for use of query languages. DataNucleus makes use of this to allow use
of the query language defined in the JPA1 specification (JPQL) with JDO persistence. JPQL is a
pseudo-OO language based around SQL, and so not using Java syntax, unlike JDOQL. To provide a
simple example, this is what you would do
This finds all "Person" objects with surname of "Jones". You specify all details in the query.
SELECT [<result>]
[FROM <candidate-class(es)>]
[WHERE <filter>]
[GROUP BY <grouping>]
[HAVING <having>]
[ORDER BY <ordering>]
The "keywords" in the query are shown in UPPER CASE are case-insensitive.
In strict JPA the entity name cannot be a MappedSuperclass entity name. That is, if you have an
abstract superclass that is persistable, you cannot query for instances of that superclass and its
subclasses. We consider this a significant shortcoming of the querying capability, and allow the
entity name to also be of a MappedSuperclass. You are unlikely to find this supported in other JPA
implementations, but then maybe that's why you chose DataNucleus?
• Can be one of Integer, Long, Short, Float, Double, Character, Byte, Boolean, String,
java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.sql.Date, java.sql.Time,
java.sql.Timestamp, or Object[]
• Can be a user defined class, that has either a constructor taking arguments of the same type as
those returned by the query (in the same order), or has a public put(Object, Object) method, or
public setXXX() methods, or public fields.
So in our example, we are returning 2 String fields, and we define our Result Class Name as follows
...
}
So here we have a result class using the constructor arguments. We could equally have provided a
class with public fields instead, or provided setXXX methods or a put method. They all work in the
same way.
101.1.6 Filter
The most important thing to remember when defining the filter for JPQL is that think how you would
write it in SQL, and its likely the same except for field names instead of column names. The
filter has to be a boolean expression, and can include the candidate entity, fields/properties, literals,
functions, parameters, operators and subqueries
101.1.7 Fields/Properties
In JPQL you refer to fields/properties in the query by referring to the field/bean name. For example,
if you are querying a candidate entity called Product and it has a field "price", then you access it like
this
price < 150.0
Note that, just like in Java, if you want to refer to a field/property of an entity you can prefix the field
by its alias
p.price < 150.0
You can also chain field references if you have an entity Product (alias = p) with a field of
(persistable) type Inventory, which has a field name, so you could do
p.inventory.name = 'Backup'
101.1.8 Operators
The operators are listed below in order of decreasing precedence.
• Navigation operator (.)
• Arithmetic operators:
• +, - unary
• *, / multiplication and division
• +, - addition and subtraction
• Comparison operators : =, >, >=, <, <=, <> (not equal), [NOT] BETWEEN, [NOT] LIKE,
[NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY, [NOT] MEMBER [OF], [NOT] EXISTS
• Logical operators:
• NOT
• AND
• OR
101.1.9 Literals
JPQL supports the following literals: IntegerLiteral, FloatingPointLiteral, BooleanLiteral,
CharacterLiteral, StringLiteral, and NullLiteral. When String literals are specified using single-string
format they should be surrounded by single-quotes '.
Named Parameters :
Query q = pm.newQuery("JPQL",
"SELECT p FROM Person p WHERE p.lastName = :surname AND o.firstName = :forename");
Map params = new HashMap();
params.put("surname", theSurname);
params.put("forename", theForename");
List<Person> results = (List<Person>)q.executeWithMap(params);
Numbered Parameters :
Query q = pm.newQuery("JPQL",
"SELECT p FROM Person p WHERE p.lastName = ?1 AND p.firstName = ?2");
List<Person> results = (List<Person>)q.execute(theSurname, theForename);
So in the first case we have parameters that are prefixed by : (colon) to identify them as a parameter
and we use that name in the parameter map passed to execute(). In the second case we have
parameters that are prefixed by ? (question mark) and are numbered starting at 1. We then pass the
parameters in to execute in that order.
Please note that you can easily add support for other functions for evaluation "in-memory" using this
DataNucleus plugin point
Please note that you can easily add support for other functions with RDBMS datastore using this
DataNucleus plugin point
which will sort primarily by field1 in ascending order, then secondarily by field2 in descending order.
Although it is not (yet) standard JPQL, DataNucleus also supports specifying a directive for where
NULL values of the ordered field/property go in the order, so the full syntax supported is
fieldName [ASC|DESC] [NULLS FIRST|NULLS LAST]
Note that this is only supported for a few RDBMS (H2, HSQLDB, PostgreSQL, DB2, Oracle, Derby).
101.1.13 Subqueries
With JPQL the user has a very flexible query syntax which allows for querying of the vast majority of
data components in a single query. In some situations it is desirable for the query to utilise the results
of a separate query in its calculations. JPQL also allows the use of subqueries. Here's an example
So we want to find all Employees that have a salary greater than the average salary. The subquery
must be in parentheses (brackets). Note that we have defined the subquery with an alias of "f",
whereas in the outer query the alias is "e".
So this returns all employees that earn more than all managers in the same department! You can also
compare with some/any, like this
So this returns all employees that earn more than any one Manager in the same department.
Query q = pm.newQuery("JPQL", "SELECT p.firstName, p.lastName FROM Person p WHERE p.age > 20");
List<Object[]> results = (List<Object[]>)q.execute();
this returns the first and last name for each Person meeting that filter. Obviously we may have some
container class that we would like the results returned in, so if we change the query to this
Query<PersonName> q = pm.newQuery("JPQL",
"SELECT p.firstName, p.lastName FROM Person p WHERE p.age > 20");
q.setResultClass(PersonName.class);
List<PersonName> results = (List<PersonName>)q.execute();
so each result is a PersonName, holding the first and last name. This result class needs to match one of
the following structures
• Constructor taking arguments of the same types and the same order as the result clause. An
instance of the result class is created using this constructor. For example
...
}
• Default constructor, and setters for the different result columns, using the alias name for each
column as the property name of the setter. For example
public PersonName()
{
}
...
}
Note that if the setter property name doesn't match the query result component name, you should use
AS {alias} in the query so they are the same.
The typical use of a JPQL query is to translate it into the native query language of the datastore and
return objects matched by the query. For many datastores it is simply impossible to support the full
JPQL syntax in the datastore native query language and so it is necessary to evaluate the query in-
memory. This means that we evaluate as much as we can in the datastore and then instantiate those
objects and evaluate further in-memory. Here we document the current capabilities of in-memory
evaluation in DataNucleus.
• Subqueries using ALL, ANY, SOME, EXISTS are not currently supported
• MEMBER OF syntax is not currently supported.
To enable evaluation in memory you specify the query hint datanucleus.query.evaluateInMemory
to true as follows
query.setHint("datanucleus.query.evaluateInMemory","true");
The "keywords" in the query are shown in UPPER CASE are case-insensitive.
The "keywords" in the query are shown in UPPER CASE are case-insensitive.
Query query = pm.newQuery("JPQL", "UPDATE Person p SET p.salary = 10000 WHERE age = 18");
Long numRowsUpdated = (LOng)query.execute();
102 Guides
.......................................................................................................................................
• Datastore Replication
• JavaEE Environments
• OSGi Environments
• Security
• Troubleshooting
• Performance Tuning
• Monitoring
• Logging
• Maven with DataNucleus
• Eclipse with DataNucleus
• IDEA with DataNucleus
• Netbeans with DataNucleus
• Tutorial with RDBMS
• Tutorial with ODF
• Tutorial with Excel
• Tutorial with MongoDB
• Tutorial with HBase
• Tutorial with Neo4J
• Tutorial with Cassandra
• 1-N Bidir FK Relation
• 1-N Bidir Join Relation
• M-N Relation
• M-N Attributed Relation
• Spatial Types Tutorial
• DAO Layer Design
Many applications make use of multiple datastores. It is a common requirement to be able to replicate
parts of one datastore in another datastore. Obviously, depending on the datastore, you could make
use of the datastores own capabilities for replication. DataNucleus provides its own extension to
JDO to allow replication from one datastore to another. This extension doesn't restrict you to using 2
datastores of the same type. You could replicate from RDBMS to XML for example, or from MySQL
to HSQLDB.
You need to make sure you have the persistence property datanucleus.attachSameDatastore set
to false if using replication
Note that the case of replication between two RDBMS of the same type is usually way more
efficiently replicated using the capabilities of the datastore itself
The following sample code will replicate all objects of type Product and Employee from PMF1 to
PMF2. These PMFs are created in the normal way so, as mentioned above, PMF1 could be for a
MySQL datastore, and PMF2 for XML. By default this will replicate the complete object graphs
reachable from these specified types.
import org.datanucleus.api.jdo.JDOReplicationManager;
...
...
}
...
}
...
}
so we have a 1-N unidirectional (Set) relation, and we define the metadata like this
<jdo>
<package name="org.datanucleus.samples">
<class name="ElementHolder" identity-type="application" detachable="true">
<inheritance strategy="new-table"/>
<field name="id" primary-key="true"/>
<field name="elements" persistence-modifier="persistent">
<collection element-type="org.datanucleus.samples.Element"/>
<join/>
</field>
</class>
<class name="SubElement">
<inheritance strategy="new-table"/>
<field name="value"/>
</class>
</package>
</jdo>
tx1.commit();
holderId = JDOHelper.getObjectId(holder);
}
finally
{
if (tx1.isActive())
{
tx1.rollback();
}
pm1.close();
}
and now we want to replicate these objects into datastore2, so we detach them from datastore1 and
attach them to datastore2, like this
tx1.begin();
tx1.commit();
}
finally
{
if (tx1.isActive())
{
tx1.rollback();
}
pm1.close();
}
pm2.makePersistent(detachedHolder);
tx2.commit();
}
finally
{
if (tx2.isActive())
{
tx2.rollback();
}
pm2.close();
}
That's all there is. These objects are now replicated into datastore2. Clearly you can extend this basic
idea and replicate large amounts of data.
• WebLogic
• JBoss 3.0/3.2
• JBoss 4.0
• JBoss 7.0
• Jonas 4.8
The provided DataNucleus JCA rar provides default resource adapter descriptors, one general, and the
other for the WebLogic JavaEE server. These resource adapter descriptors can be configured to meet
your needs, for example allowing XA transactions instead of the default Local transactions.
104.1.1 Requirements
To use DataNucleus with JCA the first thing that you will require is the datanucleus-jca-
{version}.rar file (available from the download section).
<session>
<ejb-name>DataNucleusBean</ejb-name>
...
<transaction-type>Container</transaction-type>
...
<session>
<container-transaction>
<method >
<ejb-name>DataNucleusBean</ejb-name>
...
<method-name>testDataNucleusTrans</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
You hereby define that transaction management is required for this method. The container will
automatically begin a transaction for this method. It will be commited if no error occurs or
rolled back otherwise. A potential SessionBean implementation containing methods to retrieve a
PersistenceManager then could look like this:
/**
* static final for the JNDI name of the PersistenceManagerFactory
*/
private static final String PMF_JNDI_NAME = "java:/datanucleus1";
/**
* Method to get the current InitialContext
*/
private InitialContext getInitialContext() throws NamingException
{
InitialContext initialContext = new InitialContext();
// or other code to create the InitialContext eg. new InitialContext(myProperies);
return initialContext;
}
/**
* Method to lookup the PersistenceManagerFactory
*/
private PersistenceManagerFactory getPersitenceManagerFactory(InitialContext context)
throws NamingException
{
return (PersistenceManagerFactory) context.lookup(PMF_JNDI_NAME);
}
/**
* Method to get a PersistenceManager
*/
public PersistenceManager getPersistenceManager()
throws NamingException
{
return getPersitenceManagerFactory(getInitialContext()).getPersistenceManager();
}
Make sure, you close the PersistenceManager in your bean methods. If you don't, the JavaEE server
will usually close it for you (one of the advantages), but of course not without a warning or error
message.
To avoid the need of editing multiple files, you could use XDoclet to generate your classes and
control the metadata by xdoclet tags. The method declaration then would look like this:
/**
* @ejb.interface-method
* @ejb.transaction type="Required"
*/
public viod testDataNucleusTrans()
throws Exception
{
//...
}
These instructions were adapted from a contribution by a DataNucleus user Alexander Bieber.
To define persistence properties you should make use of persistence.xml or jdoconfig.xml and refer
to the documentation for persistence properties for full details of the properties.
104.1.5 WebLogic
To use DataNucleus on Weblogic the first thing that you will require is the datanucleus-jca-
{version}.rar file. You then may need to edit the /META-INF/weblogic-ra.xml file to suit the exact
version of your WebLogic server (the included file is for WebLogic 8.1).
You then deploy the RAR file on your WebLogic server.
<tx-connection-factory>
<jndi-name>datanucleus1</jndi-name>
<adapter-display-name>DataNucleus Connector</adapter-display-name>
<config-property name="ConnectionDriverName"
type="java.lang.String">com.mysql.jdbc.Driver</config-property>
<config-property name="ConnectionURL"
type="java.lang.String">jdbc:mysql://localhost/yourdbname1</config-property>
<config-property name="UserName"
type="java.lang.String">yourusername</config-property>
<config-property name="Password"
type="java.lang.String">yourpassword</config-property>
</tx-connection-factory>
<tx-connection-factory>
<jndi-name>datanucleus2</jndi-name>
<adapter-display-name>DataNucleus Connector</adapter-display-name>
<config-property name="ConnectionDriverName"
type="java.lang.String">com.mysql.jdbc.Driver</config-property>
<config-property name="ConnectionURL"
type="java.lang.String">jdbc:mysql://localhost/yourdbname2</config-property>
<config-property name="UserName"
type="java.lang.String">yourusername</config-property>
<config-property name="Password"
type="java.lang.String">yourpassword</config-property>
</tx-connection-factory>
</connection-factories>
This example creates 3 connection factories to MySQL databases, but you can create as many or as
few as you require for your system to whichever databases you prefer (as long as they are supported
by DataNucleus). With the above definition we can then use the JNDI names java:/datanucleus,
java:/datanucleus1, and java:/datanucleus2 to refer to our datastores.
Note, that you can use separate deployment descriptor files. That means, you could for example
create the three files datanucleus1-ds.xml, datanucleus2-ds.xml and datanucleus3-ds.xml with each
declaring one PersistenceManagerFactory instance. This is useful (or even required) if you need a
distributed configuration. In this case, you can use JBoss' hot deployment feature and deploy a new
PersistenceManagerFactory, while the server is running (and working with the existing PMFs): If
you create a new *-ds.xml file (instead of modifying an existing one), the server does not undeploy
anything (and thus not interrupt ongoing work), but will only add the new connection factory to the
JNDI.
You are now set to work on DataNucleus-enabling your actual application. As we have said, you can
use the above JNDI names to refer to the datastores, so you could do something like the following to
access the PersistenceManagerFactory to one of your databases.
import javax.jdo.PersistenceManagerFactory;
These instructions were adapted from a contribution by a DataNucleus user Marco Schulze.
You are now set to work on DataNucleus-enabling your actual application. You can use the above
JNDI name to refer to the datastores, and so you could do something like the following to access the
PersistenceManagerFactory to one of your databases.
import javax.jdo.PersistenceManagerFactory;
These instructions were adapted from a contribution by a DataNucleus user Maciej Wegorkiewicz.
104.1.9 Jonas
To use DataNucleus on Jonas the first thing that you will require is the datanucleus-jca-{version}.rar
file. You then may need to edit the /META-INF/jonas-ra.xml file to suit the exact version of your
Jonas server (the included file is tested for Jonas 4.8).
You then deploy the RAR file on your Jonas server.
<connector>
<display-name>DataNucleus Connector</display-name>
<description></description>
<vendor-name>DataNucleus Team</vendor-name>
<spec-version>1.0</spec-version>
<eis-type>JDO Adaptor</eis-type>
<version>1.0</version>
<resourceadapter>
<managedconnectionfactory-class>org.datanucleus.jdo.connector.ManagedConnectionFactoryImpl</manag
<connectionfactory-interface>javax.resource.cci.ConnectionFactory</connectionfactory-interface>
<connectionfactory-impl-class>org.datanucleus.jdo.connector.PersistenceManagerFactoryImpl</connec
<connection-interface>javax.resource.cci.Connection</connection-interface>
<connection-impl-class>org.datanucleus.jdo.connector.PersistenceManagerImpl</connection-impl-clas
<transaction-support>XATransaction</transaction-support> <!-- change this line -->
...
<connector>
<display-name>DataNucleus Connector</display-name>
<description></description>
<vendor-name>DataNucleus Team</vendor-name>
<spec-version>1.0</spec-version>
<eis-type>JDO Adaptor</eis-type>
<version>1.0</version>
<resourceadapter>
<managedconnectionfactory-class>org.datanucleus.jdo.connector.ManagedConnectionFactoryImpl</manag
<connectionfactory-interface>javax.resource.cci.ConnectionFactory</connectionfactory-interface>
<connectionfactory-impl-class>org.datanucleus.jdo.connector.PersistenceManagerFactoryImpl</connec
<connection-interface>javax.resource.cci.Connection</connection-interface>
<connection-impl-class>org.datanucleus.jdo.connector.PersistenceManagerImpl</connection-impl-clas
<transaction-support>XATransaction</transaction-support>
<config-property>
<config-property-name>ConnectionFactoryName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>jndiName_for_datasource_1</config-property-value>
</config-property>
<config-property>
<config-property-name>ConnectionResourceType</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>JTA</config-property-value>
</config-property>
<config-property>
<config-property-name>ConnectionFactory2Name</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>jndiName_for_datasource_2</config-property-value>
</config-property>
...
See also :
• (RDBMS) Data Sources usage with DataNucleus
• Any dependent jar that is required by DataNucleus needs to be OSGi enabled. By this we
mean the jar needs to have the MANIFEST.MF file including ExportPackage for the packages
required by DataNucleus. Failure to have this will result in ClassNotFoundException when
trying to load its classes.
• Use jdo-api.jar v3.0.1 or later since those are OSGi-enabled
• The javax.persistence jar that is included in the DataNucleus distribution is OSGi-enabled.
• When using DataNucleus in an OSGi environments set the persistence property
datanucleus.plugin.pluginRegistryClassName to org.datanucleus.plugin.OSGiPluginRegistry
• If you redeploy a JDO-enabled OSGi application, likely you will need to refresh the javax.jdo
and maybe other bundles.
Please make use of the OSGi sample for JDO in case it is of use. Use of OSGi is notorious for class
loading oddities, so it may be necessary to refine this sample for your situation. We welcome any
feedback to improve it.
106 Troubleshooting
.......................................................................................................................................
106.2.1 Introdution
Java allocate objects in the runtime memory data area called heap. The heap is created on virtual
machine start-up. The memory allocated to objects are reclaimed by Garbage Collectors when the
object is no longer referenced (See Object References). The heap may be of a fixed size, but can also
be expanded when more memory is needed or contracted when no longer needed. If a larger heap is
needed and it cannot be allocated an OutOfMemory is thrown. See JVM Specification.
Native memory is used by the JVM to perform its operations like creation of threads, sockets, jdbc
drivers using native code, libraries using native code, etc.
The maximum size of heap memory is determined by the -Xmx on the java command line. If Xmx is
not set, then the JVM decides for the maximum heap. The heap and native memory are limited to the
maximum memory allocated by the JVM. For example, if the JVM Xmx is set to 1GB and currently
use of native memory is 256MB then the heap can only use 768MB.
106.2.2 Causes
Common causes of out of memory:
• Not enough heap - The JVM needs more memory to deal with the application requirements.
Queries returning more objects than usual can be the cause.
• Not enough PermGen - The JVM needs more memory to load class definitions.
• Memory Leaks - The application does not close the resources, like the PersistenceManager or
Queries, and the JVM cannot reclaim the memory.
• Caching - Caching in the application or inside DataNucleus holding strong references to objects.
• Garbage Collection - If no full garbage collection is performed before the OutOfMemory it can
indicate a bug in the JVM Garbage Collector.
• Memory Fragmentation - A large object needs to be placed in the memory, but the JVM cannot
allocate a continous space to it because the memory is fragmented.
• JDBC driver - a bug in the JDBC driver not flushing resources or keeping large result sets in
memory.
106.2.3 Throubleshooting
106.2.3.1 JVM
Collect garbage collection information by adding -verbosegc to the java command line. The
verbosegc flag will print garbage collections to System output.
106.2.3.3 DataNucleus
DataNucleus keeps in cache persistent objects using weak references by default. Enable debug mode
DataNucleus.Cache category to investigate the size of the cache in DataNucleus.
106.2.4 Resolution
DataNucleus can be configured to reduce the number of objects in cache. DataNucleus has cache for
persistent objects, metadata, datastore metadata, fields of type Collection or Map, or query results.
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
//...
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
for (int i=0; i<100000; i++)
{
Wardrobe wardrobe = new Wardrobe();
wardrobe.setModel("3 doors");
pm.makePersistent(wardrobe);
if (i % 10000 == 0)
{
pm.flush();
}
}
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
106.3.1 Introdution
The application pauses for short or long periods or hangs during very long time.
106.3.2 Causes
Common causes:
• Database Locking - Database waiting other transactions to release locks due to deadlock or
locking contentions.
• Garbage Collection Pauses - The garbage collection pauses the application to free memory
resources.
• Application Locking - Thread 2 waiting for resources locked by Thread 1.
106.3.3 Throubleshooting
106.4 Postgres
106.4.1.1 Problem
Exception org.postgresql.util.PSQLException: ERROR: schema "PUBLIC" does not exist raised
during transaction.
106.4.1.2 Troubleshooting
• Verify that the schema "PUBLIC" exists. If the name is lowercased ("public"), set
datanucleus.identifier.case=PreserveCase, since Postgres is case sensitive.
• Via pgAdmin Postgres tool, open a connection to the schema and verify it is acessible with
issuing a SELECT 1 statement.
106.5.1.1 Problem
CreateProcess error=87 when running DataNucleus tools under Microsoft Windows OS.
Windows has a command line length limitation, between 8K and 64K characters depending on the
Windows version, that may be triggered when running tools such as the Enhancer or the SchemaTool
with too many arguments.
106.5.1.2 Solution
When running such tools from Maven or Ant, disable the fork mechanism by setting the option
fork="false".
107.1.1 Enhancement
You should perform enhancement before runtime. That is, do not use java agent since it will enhance
classes at runtime, when you want responsiveness from your application.
#schema creation
datanucleus.schema.autoCreateAll=false
datanucleus.schema.autoCreateTables=false
datanucleus.schema.autoCreateColumns=false
datanucleus.schema.autoCreateConstraints=false
#schema validation
datanucleus.schema.validateTables=false
datanucleus.schema.validateConstraints=false
datanucleus.schema.validateColumns=false
datanucleus.rdbms.CheckExistTablesOrViews=false
datanucleus.rdbms.initializeColumnInfo=None
• If you are using "optimistic" transactions then all datastore operations will be delayed until
commit. Otherwise all datastore operations will default to being performed immediately.
If you are handling a very large number of objects in the transaction you would benefit by
either disabling "optimistic" transactions, or alternatively setting the persistence property
datanucleus.flush.mode to AUTO, or alternatively again do a manual flush every "n"
objects, like this
• If you are retrieving any object by its identity ( pm.getObjectById))and know that it
will be present in the Level2 cache, for example, you can set the persistence property
datanucleus.findObject.validateWhenCached to false and this will skip a separate call to
the datastore to validate that the object exists in the datastore.
• Commit the transaction (if running non-transactional then this happens immediately after your
persistence operation, seamlessly).
application, you should disable it. To disable reachability set the persistence property
datanucleus.persistenceByReachabilityAtCommit to false.
• DataNucleus will, by default, perform a check on any bidirectional relations to make
sure that they are set at both sides at commit. If they aren't set at both sides then they will
be made consistent. This check process can involve the (re-)loading of some instances.
You can skip this step if you always set both sides of a relation by setting the persistence
property datanucleus.manageRelationships to false.
• Objects enlisted in the transaction are put in the Level 2 cache. You can disable the level 2
cache with the persistence property datanucleus.cache.level2.type set to none
• Objects enlisted in the transaction are detached if you have the persistence property
datanucleus.detachAllOnCommit set to true. Disable this if you don't need these objects
to be detached
DataNucleus has 2 ways of handling calls to SCO Collections/Maps. The original method was to
pass all calls through to the datastore. The second method (which is now the default) is to cache
the collection/map elements/keys/values. This second method will read the elements/keys/values
once only and thereafter use the internally cached values. This second method gives significant
performance gains relative to the original method. You can configure the handling of collections/maps
as follows :-
• Globally for the PMF - this is controlled by setting the persistence property
datanucleus.cache.collections. Set it to true for caching the collections (default), and false to
pass through to the datastore.
• For the specific Collection/Map - this overrides the global setting and is controlled by adding
a MetaData <collection> or <map> extension cache. Set it to true to cache the collection data,
and false to pass through to the datastore.
The second method also allows a finer degree of control. This allows the use of lazy loading of data,
hence elements will only be loaded if they are needed. You can configure this as follows :-
• Globally for the PMF - this is controlled by setting the property
datanucleus.cache.collections.lazy. Set it to true to use lazy loading, and set it to false to load
the elements when the collection/map is initialised.
• For the specific Collection/Map - this overrides the global PMF setting and is controlled by
adding a MetaData <collection> or <map> extension cache-lazy-loading. Set it to true to use
lazy loading, and false to load once at initialisation.
• Use makeTransient to get transient versions of the objects. Note that to recurse you need to call
the makeTransient method which has a boolean argument "useFetchPlan".
Object pc = null;
try
{
PersistenceManager pm = pmf.getPersistenceManager();
pm.currentTransaction().begin();
pm.currentTransaction().commit();
}
finally
{
pm.close();
}
//read the persistent object here
System.out.prinln(pc.getName());
• Use RetainValues=true.
Object pc = null;
try
{
PersistenceManager pm = pmf.getPersistenceManager();
pm.currentTransaction().setRetainValues(true);
pm.currentTransaction().begin();
pm.currentTransaction().commit();
}
finally
{
pm.close();
}
//read the persistent object here
System.out.prinln(pc.getName());
pm.currentTransaction().commit();
}
finally
{
pm.close();
}
//read or change the detached object here
System.out.prinln(copy.getName());
• Use detachAllOnCommit.
Object pc = null;
try
{
PersistenceManager pm = pmf.getPersistenceManager();
pm.setDetachAllOnCommit(true);
pm.currentTransaction().begin();
The most expensive in terms of performance is the detachCopy because it makes copies of persistent
objects. The advantage of detachment (via detachCopy or detachAllOnCommit) is that changes made
outside the transaction can be further used to update the database in a new transaction. The other
methods also allow changes outside of the transaction, but the changed instances can't be used to
update the database.
With RetainValues=true and makeTransient no object copies are made and the object values are set
down in instances when the PersistenceManager disassociates them. Both methods are equivalent in
performance, however the makeTransient method will set the values of the object during the instant
the makeTransient method is invoked, and the RetainValues=true will set values of the object during
commit.
The bottom line is to not use detachment if instances will only be used to read values.
107.1.15 Logging
I/O consumes a huge slice of the total processing time. Therefore it is recommended to reduce or
disable logging in production. To disable the logging set the DataNucleus category to OFF in the
Log4j configuration. See Logging for more information.
log4j.category.DataNucleus=OFF
What is a benchmark? This is simply a series of persistence operations performing particular things e.g
persist n objects, or retrieve n objects. If those operations are representative of your application then the
benchmark is valid to you.
To find (or create) a benchmark appropriate to your project you need to determine the typical
persistence operations that your application will perform. Are you interested in persisting 100 objects
at once, or 1 million, for example? Then when you have a benchmark appropriate for that operation,
compare the persistence solutions.
The performance tuning guide above gives a good oversight of tuning capabilities, and also refer to
the following blog entry for our take on performance of DataNucleus AccessPlatform. And then the
later blog entry about how to tune for bulk operations
• It is essential that tests for such as Hibernate and DataNucleus performance comparable things.
Some of the original tests had the "delete" simply doing a "DELETE FROM TBL" for Hibernate
yet doing an Extent followed by delete each object individually for a JDO implementation.
This is an unfair comparison and in the source tree in JPOX SVN this is corrected. This fix was
pointed out to the PolePos SourceForge project but is not, as yet, fixed
• It is essential that schema is generated before the test, otherwise the test is no longer a
benchmark of just a persistence operation. The source tree in JPOX SVN assumes the schema
exists. This fix was pointed out to the PolePos SourceForge project but is not, as yet, fixed
• Each persistence implementation should have its own tuning options, and be able to add things
like discriminators since that is what would happen in a real application. The source tree in JPOX
SVN does this for JPOX running. Similarly a JDO implementation would tune the fetch groups
being used - this is not present in the SourceForge project but is in JPOX SVN.
• DataNucleus performance is considered to be significantly improved over JPOX particularly due
to batched inserts, and due to a rewritten query implementation that does enhanced fetching.
108 Monitoring
.......................................................................................................................................
• com.sun.management.jmxremote
• com.sun.management.jmxremote.authenticate
• com.sun.management.jmxremote.ssl
• com.sun.management.jmxremote.port=<port number>
Usage example:
Once you start your application and DataNucleus is initialized you can browse DataNucleus MBeans
using a tool called jconsole (jconsole is distributed with the Sun JDK) via the URL:
service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi
Note that the mode of usage is presented in this document as matter of example, and by no means we
recommend to disable authentication and secured communication channels. Further details on the Sun
JMX implementation and how to configure it properly can be found in here.
DataNucleus MBeans are registered in a MBean Server when DataNucleus is started up (e.g. upon
JDO PMF instantiation). To see the full list of DataNucleus MBeans, refer to the javadocs.
To enable management using MX4J you must specify the persistence property datanucleus.jmxType
as mx4j when creating the PMF, and have the mx4j and mx4j-tools jars in the CLASSPATH.
<project>
...
<dependencies>
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo-api</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>
...
</project>
The only distinction to make here is that the above is for compile time since your persistence code (if
implementation independent) will only depend on the basic persistence API. At runtime you will need
the DataNucleus artifacts present also, so this becomes
<project>
...
<dependencies>
...
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-core</artifactId>
<version>(3.9, )</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-api-jdo</artifactId>
<version>(3.9, )</version>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-rdbms</artifactId>
<version>(3.9, )</version>
<scope>runtime</scope>
</dependency>
</dependencies>
...
</project>
Obviously replace the datanucleus-rdbms jar with the jar for whichever datastore you are using. If
running your app using Maven "exec" plugin then the runtime specification may not be needed.
Please note that you can alternatively use the convenience artifact for JDO+RDBMS (or JDO+
whichever datastore you're using).
<project>
...
<dependencies>
...
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-accessplatform-jdo-rdbms</artifactId>
<version>4.0.0-release</version>
<type>pom</type>
</dependency>
</dependencies>
...
</project>
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-maven-plugin</artifactId>
<version>4.0.0-release</version>
<configuration>
<api>JDO</api>
<props>${basedir}/datanucleus.properties</props>
<log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Note that this plugin step will automatically try to bring in the latest applicable version of
datanucleus-core for use by the enhancer. It does this since you don't need to have datanucleus-core
in your POM for compilation/enhancement. If you want to use an earlier version then you need to add
exclusions to the maven-datanucleus-plugin
The executions part of that will make enhancement be performed immediately after compile, so
automatic. See also the Enhancer docs
To run the enhancer manually you do
mvn datanucleus:enhance
mvn datanucleus:schema-create
Below this you can set the location of a configuration file for Log4j to use. This is useful when you
want to debug the Enhancer/SchemaTool operations.
• Input file extensions : the enhancer accepts input defining the classes to be enhanced. This is
typically performed by passing in the JDO XML MetaData files. When you use annotations you
need to pass in class files. So you select the suffices you need
• Verbose : selecting this means you get much more output from the enhancer
• PersistenceUnit : Name of the persistence unit if enhancing a persistence-unit
Messages from the SchemaTool process will be written to the Eclipse Console.
111.1.1 Background
An application can be JDO-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the DataNucleus Eclipse plugin. Alternatively the project
could use Ant, Maven or some other build tool. In this case this tutorial should be used as a guiding
way for using DataNucleus in the application. The JDO process is quite straightforward.
111.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Inventory
{
@PrimaryKey
String name = null;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Product
{
@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.INCREMENT)
long id;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @PersistenceCapable and their primary key
field(s) with @PrimaryKey. In addition we defined a valueStrategy for Product field id so that it will
have its values generated automatically. In this tutorial we are using application identity which means
that all objects of these classes will have their identity defined by the primary key field(s). You can
read more in datastore identity and application identity when designing your systems persistence.
Note that you could equally use a properties file to define the persistence with JDO, but in this tutorial
we use persistence.xml for convenience.
src/main/java/org/datanucleus/samples/jdo/tutorial/Book.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jdo/tutorial/Book.class
target/classes/org/datanucleus/samples/jdo/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jdo/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven2 :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jdo.jar:lib/jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jdo.jar;lib\jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
This command enhances the .class files that have @PersistenceCapable annotations. If you
accidentally omitted this step, at the point of running your application and trying to persist an
object, you would get a ClassNotPersistenceCapableException thrown. The use of the enhancer is
documented in more detail in the Enhancer Guide. The output of this step are a set of class files that
represent PersistenceCapable classes.
Now that the application has a PersistenceManager it can persist objects. This is performed as follows
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
Inventory inv = new Inventory("My Inventory");
Product product = new Product("Sony Discman", "A standard discman from Sony", 49.99);
inv.getProducts().add(product);
pm.makePersistent(inv);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
• We have persisted the Inventory but since this referenced the Product then that is also persisted.
• The finally step is important to tidy up any connection to the datastore, and close the
PersistenceManager
If you want to retrieve an object from persistent storage, something like this will give what you need.
This uses a "Query", and retrieves all Product objects that have a price below 150.00, ordering them in
ascending price order.
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
pm.deletePersistent(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JDO book will provide many examples.
Using Ant (you need the included "persistence.xml" to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/jdo-api.jar:lib/datanucleus-core.jar:lib/datanucleus-rdbms.jar:
lib/datanucleus-api-jdo.jar:lib/{jdbc-driver}.jar:target/classes/:.
org.datanucleus.samples.jdo.tutorial.Main
Manually on Windows :
java -cp lib\jdo-api.jar;lib\datanucleus-core.jar;lib\datanucleus-rdbms.jar;
lib\datanucleus-api-jdo.jar;lib\{jdbc-driver}.jar;target\classes\;.
org.datanucleus.samples.jdo.tutorial.Main
Output :
DataNucleus Tutorial
=============
Persisting products
Product and Book have been persisted
End of Tutorial
In the above simple tutorial we showed how to employ JDO and persist objects to an RDBMS.
Obviously this just scratches the surface of what you can do, and to use JDO requires minimal work
from the user. In this second part we show some further things that you are likely to want to do.
<?xml version="1.0"?>
<!DOCTYPE orm PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/orm_2_0.dtd">
<orm>
<package name="org.datanucleus.samples.jdo.tutorial">
<class name="Inventory" identity-type="datastore" table="INVENTORIES">
<inheritance strategy="new-table"/>
<field name="name">
<column name="INVENTORY_NAME" length="100" jdbc-type="VARCHAR"/>
</field>
<field name="products">
<join/>
</field>
</class>
With JDO you have various options as far as where this XML MetaData files is placed in the file
structure, and whether they refer to a single class, or multiple classes in a package. With the above
example, we have both classes specified in the same file package-hsql.orm, in the package these
classes are in, since we want to persist to HSQL.
111.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the SchemaTool to generate the tables where these domain objects will
be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked from Maven2/
Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to update the
persistence.xml file with your database details. Here we have a sample file (for HSQLDB)
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven2 :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-rdbms.jar:
lib/datanucleus-jdo-api.jar:lib/jdo-api.jar:lib/{jdbc_driver.jar}
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-rdbms.jar;
lib\datanucleus-api-jdo.jar;lib\jdo-api.jar;lib\{jdbc_driver.jar}
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
This will generate the required tables, indexes, and foreign keys for the classes defined in the JDO
Meta-Data file. The generated schema in this case will be as follows
112.1.1 Background
An application can be JDO-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the DataNucleus Eclipse plugin. Alternatively the project
could use Ant, Maven or some other build tool. In this case this tutorial should be used as a guiding
way for using DataNucleus in the application. The JDO process is quite straightforward.
112.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Inventory
{
@PrimaryKey
String name = null;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Product
{
@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.INCREMENT)
long id;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @PersistenceCapable and their primary key
field(s) with @PrimaryKey. In addition we defined a valueStrategy for Product field id so that it will
have its values generated automatically. In this tutorial we are using application identity which means
that all objects of these classes will have their identity defined by the primary key field(s). You can
read more in datastore identity and application identity when designing your systems persistence.
Note that you could equally use a properties file to define the persistence with JDO, but in this tutorial
we use persistence.xml for convenience.
src/main/java/org/datanucleus/samples/jdo/tutorial/Book.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jdo/tutorial/Book.class
target/classes/org/datanucleus/samples/jdo/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jdo/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven2 :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jdo.jar:lib/jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jdo.jar;lib\jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
This command enhances the .class files that have @PersistenceCapable annotations. If you
accidentally omitted this step, at the point of running your application and trying to persist an
object, you would get a ClassNotPersistenceCapableException thrown. The use of the enhancer is
documented in more detail in the Enhancer Guide. The output of this step are a set of class files that
represent PersistenceCapable classes.
Now that the application has a PersistenceManager it can persist objects. This is performed as follows
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
Inventory inv = new Inventory("My Inventory");
Product product = new Product("Sony Discman", "A standard discman from Sony", 49.99);
inv.getProducts().add(product);
pm.makePersistent(inv);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
• We have persisted the Inventory but since this referenced the Product then that is also persisted.
• The finally step is important to tidy up any connection to the datastore, and close the
PersistenceManager
If you want to retrieve an object from persistent storage, something like this will give what you need.
This uses a "Query", and retrieves all Product objects that have a price below 150.00, ordering them in
ascending price order.
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
pm.deletePersistent(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JDO book will provide many examples.
Using Ant (you need the included "persistence.xml" to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/jdo-api.jar:lib/datanucleus-core.jar:lib/datanucleus-odf.jar:
lib/datanucleus-api-jdo.jar:lib/odfdom.jar:target/classes/:.
org.datanucleus.samples.jdo.tutorial.Main
Manually on Windows :
java -cp lib\jdo-api.jar;lib\datanucleus-core.jar;lib\datanucleus-odf.jar;
lib\datanucleus-api-jdo.jar;lib\odfdom.jar;target\classes\;.
org.datanucleus.samples.jdo.tutorial.Main
Output :
DataNucleus Tutorial
=============
Persisting products
Product and Book have been persisted
End of Tutorial
In the above simple tutorial we showed how to employ JDO and persist objects to ODF. Obviously
this just scratches the surface of what you can do, and to use JDO requires minimal work from the
user. In this second part we show some further things that you are likely to want to do.
<?xml version="1.0"?>
<!DOCTYPE orm PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/orm_2_0.dtd">
<orm>
<package name="org.datanucleus.samples.jdo.tutorial">
<class name="Inventory" table="Inventories">
<field name="name">
<column name="Name" length="100"/>
</field>
<field name="products"/>
</class>
With JDO you have various options as far as where this XML MetaData files is placed in the file
structure, and whether they refer to a single class, or multiple classes in a package. With the above
example, we have both classes specified in the same file package-odf.orm, in the package these
classes are in, since we want to persist to ODF.
112.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the SchemaTool to generate the tables where these domain objects will
be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked from Maven2/
Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to update the
persistence.xml file with your database details.
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven2 :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-odf.jar:
lib/datanucleus-jdo-api.jar:lib/jdo-api.jar:lib/odfdom.jar
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-odf.jar;
lib\datanucleus-api-jdo.jar;lib\jdo-api.jar;lib\odfdom.jar
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
This will generate the required tables, etc for the classes defined in the JDO Meta-Data file.
113.1.1 Background
An application can be JDO-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the DataNucleus Eclipse plugin. Alternatively the project
could use Ant, Maven or some other build tool. In this case this tutorial should be used as a guiding
way for using DataNucleus in the application. The JDO process is quite straightforward.
113.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Inventory
{
@PrimaryKey
String name = null;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Product
{
@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.INCREMENT)
long id;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @PersistenceCapable and their primary key
field(s) with @PrimaryKey. In addition we defined a valueStrategy for Product field id so that it will
have its values generated automatically. In this tutorial we are using application identity which means
that all objects of these classes will have their identity defined by the primary key field(s). You can
read more in datastore identity and application identity when designing your systems persistence.
Note that you could equally use a properties file to define the persistence with JDO, but in this tutorial
we use persistence.xml for convenience.
src/main/java/org/datanucleus/samples/jdo/tutorial/Book.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jdo/tutorial/Book.class
target/classes/org/datanucleus/samples/jdo/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jdo/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven2 :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jdo.jar:lib/jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jdo.jar;lib\jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
This command enhances the .class files that have @PersistenceCapable annotations. If you
accidentally omitted this step, at the point of running your application and trying to persist an
object, you would get a ClassNotPersistenceCapableException thrown. The use of the enhancer is
documented in more detail in the Enhancer Guide. The output of this step are a set of class files that
represent PersistenceCapable classes.
Now that the application has a PersistenceManager it can persist objects. This is performed as follows
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
Inventory inv = new Inventory("My Inventory");
Product product = new Product("Sony Discman", "A standard discman from Sony", 49.99);
inv.getProducts().add(product);
pm.makePersistent(inv);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
• We have persisted the Inventory but since this referenced the Product then that is also persisted.
• The finally step is important to tidy up any connection to the datastore, and close the
PersistenceManager
If you want to retrieve an object from persistent storage, something like this will give what you need.
This uses a "Query", and retrieves all Product objects that have a price below 150.00, ordering them in
ascending price order.
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
pm.deletePersistent(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JDO book will provide many examples.
Using Ant (you need the included "persistence.xml" to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/jdo-api.jar:lib/datanucleus-core.jar:lib/datanucleus-excel.jar:
lib/datanucleus-api-jdo.jar:lib/poi.jar:target/classes/:.
org.datanucleus.samples.jdo.tutorial.Main
Manually on Windows :
java -cp lib\jdo-api.jar;lib\datanucleus-core.jar;lib\datanucleus-excel.jar;
lib\datanucleus-api-jdo.jar;lib\poi.jar;target\classes\;.
org.datanucleus.samples.jdo.tutorial.Main
Output :
DataNucleus Tutorial
=============
Persisting products
Product and Book have been persisted
End of Tutorial
In the above simple tutorial we showed how to employ JDO and persist objects to Excel. Obviously
this just scratches the surface of what you can do, and to use JDO requires minimal work from the
user. In this second part we show some further things that you are likely to want to do.
<?xml version="1.0"?>
<!DOCTYPE orm PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/orm_2_0.dtd">
<orm>
<package name="org.datanucleus.samples.jdo.tutorial">
<class name="Inventory" table="Inventories">
<field name="name">
<column name="Name" length="100"/>
</field>
<field name="products"/>
</class>
With JDO you have various options as far as where this XML MetaData files is placed in the file
structure, and whether they refer to a single class, or multiple classes in a package. With the above
example, we have both classes specified in the same file package-excel.orm, in the package these
classes are in, since we want to persist to Excel.
113.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the SchemaTool to generate the tables where these domain objects will
be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked from Maven2/
Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to update the
persistence.xml file with your database details.
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven2 :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-excel.jar:
lib/datanucleus-jdo-api.jar:lib/jdo-api.jar:lib/poi.jar
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-excel.jar;
lib\datanucleus-api-jdo.jar;lib\jdo-api.jar;lib\poi.jar
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
This will generate the required tables, etc for the classes defined in the JDO Meta-Data file.
114.1.1 Background
An application can be JDO-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the DataNucleus Eclipse plugin. Alternatively the project
could use Ant, Maven or some other build tool. In this case this tutorial should be used as a guiding
way for using DataNucleus in the application. The JDO process is quite straightforward.
114.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Inventory
{
@PrimaryKey
String name = null;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Product
{
@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.INCREMENT)
long id;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @PersistenceCapable and their primary key
field(s) with @PrimaryKey. In addition we defined a valueStrategy for Product field id so that it will
have its values generated automatically. In this tutorial we are using application identity which means
that all objects of these classes will have their identity defined by the primary key field(s). You can
read more in datastore identity and application identity when designing your systems persistence.
Note that you could equally use a properties file to define the persistence with JDO, but in this tutorial
we use persistence.xml for convenience.
src/main/java/org/datanucleus/samples/jdo/tutorial/Book.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jdo/tutorial/Book.class
target/classes/org/datanucleus/samples/jdo/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jdo/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven2 :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jdo.jar:lib/jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jdo.jar;lib\jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
This command enhances the .class files that have @PersistenceCapable annotations. If you
accidentally omitted this step, at the point of running your application and trying to persist an
object, you would get a ClassNotPersistenceCapableException thrown. The use of the enhancer is
documented in more detail in the Enhancer Guide. The output of this step are a set of class files that
represent PersistenceCapable classes.
Now that the application has a PersistenceManager it can persist objects. This is performed as follows
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
Inventory inv = new Inventory("My Inventory");
Product product = new Product("Sony Discman", "A standard discman from Sony", 49.99);
inv.getProducts().add(product);
pm.makePersistent(inv);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
• We have persisted the Inventory but since this referenced the Product then that is also persisted.
• The finally step is important to tidy up any connection to the datastore, and close the
PersistenceManager
If you want to retrieve an object from persistent storage, something like this will give what you need.
This uses a "Query", and retrieves all Product objects that have a price below 150.00, ordering them in
ascending price order.
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
pm.deletePersistent(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JDO book will provide many examples.
Using Ant (you need the included "persistence.xml" to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/jdo-api.jar:lib/datanucleus-core.jar:lib/datanucleus-mongodb.jar:
lib/datanucleus-api-jdo.jar:lib/{mongodb_jars}:target/classes/:.
org.datanucleus.samples.jdo.tutorial.Main
Manually on Windows :
java -cp lib\jdo-api.jar;lib\datanucleus-core.jar;lib\datanucleus-mongodb.jar;
lib\datanucleus-api-jdo.jar;lib\{mongodb_jars};target\classes\;.
org.datanucleus.samples.jdo.tutorial.Main
Output :
DataNucleus Tutorial
=============
Persisting products
Product and Book have been persisted
End of Tutorial
In the above simple tutorial we showed how to employ JDO and persist objects to MongoDB.
Obviously this just scratches the surface of what you can do, and to use JDO requires minimal work
from the user. In this second part we show some further things that you are likely to want to do.
<?xml version="1.0"?>
<!DOCTYPE orm PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/orm_2_0.dtd">
<orm>
<package name="org.datanucleus.samples.jdo.tutorial">
<class name="Inventory" identity-type="datastore" table="INVENTORIES">
<inheritance strategy="new-table"/>
<field name="name">
<column name="INVENTORY_NAME" length="100" jdbc-type="VARCHAR"/>
</field>
<field name="products">
<join/>
</field>
</class>
With JDO you have various options as far as where this XML MetaData files is placed in the file
structure, and whether they refer to a single class, or multiple classes in a package. With the above
example, we have both classes specified in the same file package-mongodb.orm, in the package these
classes are in, since we want to persist to MongoDB.
114.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the SchemaTool to generate the tables where these domain objects will
be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked from Maven2/
Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to update the
persistence.xml file with your database details.
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven2 :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-mongodb.jar:
lib/datanucleus-jdo-api.jar:lib/jdo-api.jar:lib/{mongodb_driver.jar}
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-mongodb.jar;
lib\datanucleus-api-jdo.jar;lib\jdo-api.jar;lib\{mongodb_driver.jar}
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
This will generate the required tables, etc for the classes defined in the JDO Meta-Data file.
115.1.1 Background
An application can be JDO-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the DataNucleus Eclipse plugin. Alternatively the project
could use Ant, Maven or some other build tool. In this case this tutorial should be used as a guiding
way for using DataNucleus in the application. The JDO process is quite straightforward.
115.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Inventory
{
@PrimaryKey
String name = null;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Product
{
@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.INCREMENT)
long id;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @PersistenceCapable and their primary key
field(s) with @PrimaryKey. In addition we defined a valueStrategy for Product field id so that it will
have its values generated automatically. In this tutorial we are using application identity which means
that all objects of these classes will have their identity defined by the primary key field(s). You can
read more in datastore identity and application identity when designing your systems persistence.
Note that you could equally use a properties file to define the persistence with JDO, but in this tutorial
we use persistence.xml for convenience.
src/main/java/org/datanucleus/samples/jdo/tutorial/Book.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Product.java
src/main/resources/persistence.xml
target/classes/org/datanucleus/samples/jdo/tutorial/Book.class
target/classes/org/datanucleus/samples/jdo/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jdo/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven2 :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jdo.jar:lib/jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jdo.jar;lib\jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
This command enhances the .class files that have @PersistenceCapable annotations. If you
accidentally omitted this step, at the point of running your application and trying to persist an
object, you would get a ClassNotPersistenceCapableException thrown. The use of the enhancer is
documented in more detail in the Enhancer Guide. The output of this step are a set of class files that
represent PersistenceCapable classes.
Now that the application has a PersistenceManager it can persist objects. This is performed as follows
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
Inventory inv = new Inventory("My Inventory");
Product product = new Product("Sony Discman", "A standard discman from Sony", 49.99);
inv.getProducts().add(product);
pm.makePersistent(inv);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
• We have persisted the Inventory but since this referenced the Product then that is also persisted.
• The finally step is important to tidy up any connection to the datastore, and close the
PersistenceManager
If you want to retrieve an object from persistent storage, something like this will give what you need.
This uses a "Query", and retrieves all Product objects that have a price below 150.00, ordering them in
ascending price order.
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
pm.deletePersistent(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JDO book will provide many examples.
Using Ant (you need the included "persistence.xml" to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/jdo-api.jar:lib/datanucleus-core.jar:lib/datanucleus-hbase.jar:
lib/datanucleus-api-jdo.jar:lib/{hbase_jars}:target/classes/:.
org.datanucleus.samples.jdo.tutorial.Main
Manually on Windows :
java -cp lib\jdo-api.jar;lib\datanucleus-core.jar;lib\datanucleus-hbase.jar;
lib\datanucleus-api-jdo.jar;lib\{hbase_jars};target\classes\;.
org.datanucleus.samples.jdo.tutorial.Main
Output :
DataNucleus Tutorial
=============
Persisting products
Product and Book have been persisted
End of Tutorial
In the above simple tutorial we showed how to employ JDO and persist objects to HBase. Obviously
this just scratches the surface of what you can do, and to use JDO requires minimal work from the
user. In this second part we show some further things that you are likely to want to do.
<?xml version="1.0"?>
<!DOCTYPE orm PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/orm_2_0.dtd">
<orm>
<package name="org.datanucleus.samples.jdo.tutorial">
<class name="Inventory" identity-type="datastore" table="INVENTORIES">
<inheritance strategy="new-table"/>
<field name="name">
<column name="INVENTORY_NAME" length="100" jdbc-type="VARCHAR"/>
</field>
<field name="products">
<join/>
</field>
</class>
With JDO you have various options as far as where this XML MetaData files is placed in the file
structure, and whether they refer to a single class, or multiple classes in a package. With the above
example, we have both classes specified in the same file package-hbase.orm, in the package these
classes are in, since we want to persist to HBase.
115.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the SchemaTool to generate the tables where these domain objects will
be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked from Maven2/
Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to update the
persistence.xml file with your database details
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven2 :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-hbase.jar:
lib/datanucleus-jdo-api.jar:lib/jdo-api.jar:lib/{hbase_driver.jar}
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-hbase.jar;
lib\datanucleus-api-jdo.jar;lib\jdo-api.jar;lib\{hbase_driver.jar}
org.datanucleus.store.schema.SchemaTool
-create -pu Tutorial
Note that "hbase_driver" typically means hbase.jar, hadoop-core.jar, zookeeper.jar and commons-logging.ja
This will generate the required tables, etc for the classes defined in the JDO Meta-Data file.
116.1.1 Background
An application can be JDO-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the DataNucleus Eclipse plugin. Alternatively the project
could use Ant, Maven or some other build tool. In this case this tutorial should be used as a guiding
way for using DataNucleus in the application. The JDO process is quite straightforward.
116.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
package org.datanucleus.samples.jdo.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Inventory
{
@PrimaryKey
String name = null;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Product
{
@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.NATIVE)
long id;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @PersistenceCapable and their primary key
field(s) with @PrimaryKey. In addition we defined a valueStrategy for Product field id so that it will
have its values generated automatically. In this tutorial we are using application identity which means
that all objects of these classes will have their identity defined by the primary key field(s). You can
read more in datastore identity and application identity when designing your systems persistence.
Note that you could equally use a properties file to define the persistence with JDO, but in this tutorial
we use persistence.xml for convenience.
src/main/java/org/datanucleus/samples/jdo/tutorial/Book.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jdo/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jdo/tutorial/Book.class
target/classes/org/datanucleus/samples/jdo/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jdo/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven2 :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jdo.jar:lib/jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jdo.jar;lib\jdo-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer -pu Tutorial
This command enhances the .class files that have @PersistenceCapable annotations. If you
accidentally omitted this step, at the point of running your application and trying to persist an
object, you would get a ClassNotPersistenceCapableException thrown. The use of the enhancer is
documented in more detail in the Enhancer Guide. The output of this step are a set of class files that
represent PersistenceCapable classes.
Now that the application has a PersistenceManager it can persist objects. This is performed as follows
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
Inventory inv = new Inventory("My Inventory");
Product product = new Product("Sony Discman", "A standard discman from Sony", 49.99);
inv.getProducts().add(product);
pm.makePersistent(inv);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
• We have persisted the Inventory but since this referenced the Product then that is also persisted.
• The finally step is important to tidy up any connection to the datastore, and close the
PersistenceManager
If you want to retrieve an object from persistent storage, something like this will give what you need.
This uses a "Query", and retrieves all Product objects that have a price below 150.00, ordering them in
ascending price order.
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
pm.deletePersistent(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JDO book will provide many examples.
Using Ant (you need the included "persistence.xml" to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/jdo-api.jar:lib/datanucleus-core.jar:lib/datanucleus-neo4j.jar:
lib/datanucleus-api-jdo.jar:lib/{neo4j_jars}:target/classes/:.
org.datanucleus.samples.jdo.tutorial.Main
Manually on Windows :
java -cp lib\jdo-api.jar;lib\datanucleus-core.jar;lib\datanucleus-neo4j.jar;
lib\datanucleus-api-jdo.jar;lib\{neo4j_jars};target\classes\;.
org.datanucleus.samples.jdo.tutorial.Main
Output :
DataNucleus Tutorial
=============
Persisting products
Product and Book have been persisted
End of Tutorial
In the above simple tutorial we showed how to employ JDO and persist objects to Neo4J. Obviously
this just scratches the surface of what you can do, and to use JDO requires minimal work from the
user. In this second part we show some further things that you are likely to want to do.
1. Step 6 : Controlling the property names.
<?xml version="1.0"?>
<!DOCTYPE orm PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/orm_2_0.dtd">
<orm>
<package name="org.datanucleus.samples.jdo.tutorial">
<class name="Inventory" identity-type="datastore">
<inheritance strategy="new-table"/>
<field name="name">
<column name="INVENTORY_NAME"/>
</field>
<field name="products">
<join/>
</field>
</class>
With JDO you have various options as far as where this XML MetaData files is placed in the file
structure, and whether they refer to a single class, or multiple classes in a package. With the above
example, we have both classes specified in the same file package-neo4j.orm, in the package these
classes are in, since we want to persist to Neo4J.
This guide demonstrates a 1-N collection relationship between 2 classes. In this sample we have Pack
and Card such that each Pack can contain many Cards. In addition each Card has a Pack that it
belongs to. We demonstrate the classes themselves, and the MetaData necessary to persist them to the
datastore in the way that we require. In this case we are going to persist the relation to an RDBMS
using a ForeignKey.
1. Classes - Design your Java classes to represent what you want to model in your system.
Persistence doesn't have much of an impact on this stage, but we'll analyse the very minor
influence it does have.
2. Object Identity - Decide how the identities of your objects of these classes will be defined. Do
you want JDO to give them id's or will you do it yourself.
3. Meta-Data - Define how your objects of these classes will be persisted.
1. New Database Schema - you have a clean sheet of paper and can have them persisted with
no constraints.
2. Existing Database Schema - you have existing tables that you need the objects persisted to.
package org.datanucleus.samples.packofcards.inverse;
The first thing that we need to do is add a default constructor. This is a requirement of JDO. This can
be private if we wish, so we add
...
}
public class Card
{
private Card()
{
}
...
}
<?xml version="1.0"?>
<!DOCTYPE jdo PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/jdo_2_0.dtd">
<jdo>
Now let's define the persistence for our Pack class. We are going to use datastore identity here,
meaning that DataNucleus will assign id's to each Pack object persisted. We define it as follows
<package name="org.datanucleus.samples.packofcards.inverse">
<class name="Pack" identity-type="datastore">
<field name="name" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="description" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="cards" persistence-modifier="persistent" mapped-by="pack">
<collection element-type="org.datanucleus.samples.packofcards.inverse.Card">
</collection>
</field>
</class>
Here we've defined that our name field will be persisted to a VARCHAR(100) column, our
description field will be persisted to a VARCHAR(255) column, and that our cards field is a
Collection containing org.datanucleus.examples.packofcards.inverse.Card objects. In addition,
it specifies that there is a pack field in the Card class (the mapped-by attribute) that gives the
related pack (with the Pack being the owner of the relationship). This final information is to inform
DataNucleus to link the table for this class (via a foreign key) to the table for Card class. This is what
is termed a ForeignKey relationship. Please refer to the 1-N Relationships Guide for more details on
this. We'll discuss join table relationships in a different example.
Now lets define the persistence for our Card class. We are going to use datastore identity here,
meaining that DataNucleus will assign the id's for any object of type Card. We define it as follows
Here we've defined that our suit field will be persisted to a VARCHAR(10) column, our number field
will be persisted to a VARCHAR(20) column.
We finally terminate the Meta-Data file with the closing tag
</jdo>
mechanism and we want to use those tables (because they have data in them). Our existing tables are
shown below.
We will take the Meta-Data that was described in the previous section (New Schema) and continue
from there. To recap, here is what we arrived at
<?xml version="1.0"?>
<!DOCTYPE jdo PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/jdo_2_0.dtd">
<jdo>
<package name="org.datanucleus.samples.packofcards.inverse">
<class name="Pack" identity-type="datastore">
<field name="name" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="description" persistence-modifier="persistent">
<column length="255" jdbc-type="VARCHAR"/>
</field>
<field name="cards" persistence-modifier="persistent" mapped-by="pack">
<collection element-type="org.datanucleus.samples.packofcards.inverse.Card">
</collection>
</field>
</class>
<class name="Card" identity-type="datastore">
<field name="suit">
<column length="10" jdbc-type="VARCHAR"/>
</field>
<field name="number">
<column length="20" jdbc-type="VARCHAR"/>
</field>
<field name="pack">
</field>
</class>
</package>
</jdo>
The first thing we need to do is map the Pack class to the table that we have in our database. It needs
to be mapped to a table called "DECK", with columns "IDENTIFIERNAME" and "DETAILS",
and the identity column that DataNucleus uses needs to be called IDENTIFIER_ID. We do this by
changing the Meta-Data to be
So we made use of the attribute table (of element class) and name (of element column) to align to
the table that is there. In addition we made use of the datastore-identity element to map the identity
column name. Lets now dothe same for the class Card. In our database we want this to map to a table
called "PLAYINGCARD", with columns "SET" and "VALUE". So we do the same thing to its Meta-
Data
OK, so we've now mapped our 2 classes to their tables. This completes our job. The only other aspect
that is likely to be met is where a column in the database is of a particular type, but we'll cover that in
a different example.
One thing worth mentioning is the difference if our Collection class was a List, ArrayList, Vector, etc.
In this case we need to specify the ordering column for maintaining the order within the List. In our
case we want to specify this column to be called "IDX", so we do it like this.
This guide demonstrates an M-N collection relationship between 2 classes. In this sample we have
Supplier and Customer such that each Customer can contain many Suppliers. In addition each
Supplier can have many Customers. We demonstrate the classes themselves, and the MetaData
necessary to persist them to the datastore in the way that we require. In this example we use XML
metadata, but you could easily use annotations
1. Classes - Design your Java classes to represent what you want to model in your system. JDO
doesn't have much of an impact on this, but we'll analyse the very minor influence it does have.
2. Meta-Data - Define how your objects of these classes will be persisted.
1. New Database Schema - you have a clean sheet of paper and can have them persisted with
no constraints.
2. Existing Database Schema - you have existing tables that you need the objects persisted to.
3. Managing the Relationship - How we add/remove elements to/from the M-N relation.
package org.datanucleus.samples.m_to_n;
The first thing that we need to do is add a default constructor. This is a requirement of JDO. In our
case we are using the DataNucleus enhancer and this will automatically add the default constructor
when not present, so we omit this.
In this example we don't care about the "identity" type chosen so we will use datastore-identity.
Please refer to the documentation for examples of application and datastore identity for how to
specify them.
<?xml version="1.0"?>
<!DOCTYPE jdo PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/jdo_2_0.dtd">
<jdo>
Now let's define the persistence for our Customer class. We define it as follows
<package name="org.datanucleus.samples.m_to_n">
<class name="Customer" identity-type="datastore">
<field name="name" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="description" persistence-modifier="persistent">
<column length="255" jdbc-type="VARCHAR"/>
</field>
<field name="suppliers" persistence-modifier="persistent" mapped-by="customers">
<collection element-type="org.datanucleus.samples.m_to_n.Supplier"/>
<join/>
</field>
</class>
Here we've defined that our name field will be persisted to a VARCHAR(100) column, our
description field will be persisted to a VARCHAR(255) column, and that our suppliers field is a
Collection containing org.datanucleus.examples.m_to_n.Supplier objects. In addition, it specifies
that there is a customers field in the Supplier class (the mapped-by attribute) that gives the related
customers for the Supplier. This final information is to inform DataNucleus to link the table for this
class to the table for Supplier class. This is what is termed an M-N relationship. Please refer to the
M-N Relationships Guide for more details on this.
Now lets define the persistence for our Supplier class. We define it as follows
Here we've defined that our name field will be persisted to a VARCHAR(100) column, our address
field will be persisted to a VARCHAR(100) column.
We finally terminate the Meta-Data file with the closing tag
</jdo>
We will take the Meta-Data that was described in the previous section (New Schema) and continue
from there. To recap, here is what we arrived at
<?xml version="1.0"?>
<!DOCTYPE jdo PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/jdo_2_0.dtd">
<jdo>
<package name="org.datanucleus.samples.m_to_n">
<class name="Customer" identity-type="datastore">
<field name="name" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="description" persistence-modifier="persistent">
<column length="255" jdbc-type="VARCHAR"/>
</field>
<field name="suppliers" persistence-modifier="persistent" mapped-by="customers">
<collection element-type="org.datanucleus.samples.m_to_n.Supplier"/>
<join/>
</field>
</class>
The first thing we need to do is map the Customer class to the table that we have in our database.
It needs to be mapped to a table called "CUSTOMER", with columns "NAME" and "DESC", and
the identity column that DataNucleus uses needs to be called CUST_ID. We do this by changing the
Meta-Data to be
We now need to define the mapping for the join table storing the relationship information. So we
make use of the "table" attribute of field to define this, and use the join and element subelements to
define the columns of the join table. Like this
Lets now dothe same for the class Supplier. In our database we want this to map to a table called
"SUPPLIER", with columns "SUPP_ID" (identity), "NAME" and "ADDR". We need to do nothing
more for the join table since it is shared and we have defined its table/columns above.
OK, so we've now mapped our 2 classes to their tables. This completes our job. The only other aspect
that is likely to be met is where a column in the database is of a particular type, but we'll cover that in
a different example.
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
Object[] custIds = new Object[2];
Object[] suppIds = new Object[3];
try
{
tx.begin();
Supplier supp1 = new Supplier("Stationery Direct", "123 The boulevard, Milton Keynes, UK");
Supplier supp2 = new Supplier("Grocery Wholesale", "56 Jones Industrial Estate, London, UK");
Supplier supp3 = new Supplier("Makro", "1 Parkville, Wembley, UK");
pm.makePersistent(cust1);
pm.makePersistent(cust2);
pm.makePersistent(supp1);
pm.makePersistent(supp2);
pm.makePersistent(supp3);
tx.commit();
custIds[0] = JDOHelper.getObjectId(cust1);
custIds[1] = JDOHelper.getObjectId(cust2);
suppIds[0] = JDOHelper.getObjectId(supp1);
suppIds[1] = JDOHelper.getObjectId(supp2);
suppIds[2] = JDOHelper.getObjectId(supp3);
}
catch (Exception e)
{
// Handle any errors
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
OK. We've now persisted some Customers and Suppliers into our datastore. We now need to
establish the relations.
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.commit();
}
catch (Exception e)
{
// Handle any errors
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
You note that we set both sides of the relation. This is important since JDO doesnt define support for
"managed relations" before JDO2.1. We could have adapted the Customer method addSupplier to
add both sides of the relation (or alternatively via Supplier method addCustomer) to simplify this
process.
Let's now assume that over time we want to change our relationships.
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
// Remove the relation from customer1 to supplier2, and add relation to supplier3
cust1.removeSupplier(supp2);
supp2.removeCustomer(cust1);
cust1.addSupplier(supp3);
supp3.addCustomer(cust1);
tx.commit();
}
catch (Exception e)
{
// Handle any errors
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
So now we have customer1 with a relation to supplier3, and we have customer2 with relations to
supplier1 and suppier3. That should give enough idea of how to manage the relations. The most
important thing with any bidirectional relation is to set both sides of the relation.
DataNucleus provides support for standard JDO M-N relations where we have a relation between,
for example, Customer and Supplier, where a Customer has many Suppliers and a Supplier has many
Customers. A slight modification on this is where you have the relation carrying some additional
attributes of the relation. Let's take some classes
...
}
...
}
Now we obviously cant define an "attributed relation" using Java and just these classes so we invent
an intermediate "associative" class, that will also contain the attributes.
<jdo>
<package name="mydomain.business">
<class name="Customer" detachable="true" table="CUSTOMER">
<field name="id" primary-key="true" value-strategy="increment" column="ID"/>
<field name="name" column="NAME"/>
<field name="supplierRelations" persistence-modifier="persistent" mapped-by="customer">
<collection element-type="BusinessRelation"/>
</field>
</class>
So we've used a 1-N "CompoundIdentity" relation between Customer and BusinessRelation, and
similarly between Supplier and BusinessRelation meaning that BusinessRelation has a composite PK
define like this
public PK()
{
}
public PK(String s)
{
StringTokenizer st = new StringTokenizer(s, "::");
this.customer = new LongIdentity(Customer.class, st.nextToken());
this.supplier = new LongIdentity(Supplier.class, st.nextToken());
}
BusinessRelation rel_1_1 = new BusinessRelation(cust1, supp1, "Very Friendly", "Hilton Hotel, London"
cust1.addRelation(rel_1_1);
supp1.addRelation(rel_1_1);
pm.makePersistent(rel_1_1);
tx.commit();
}
finally
{
if (tx1.isActive())
{
tx1.rollback();
}
pm1.close();
}
This will now have persisted an entry in table "CUSTOMER", an entry in table "SUPPLIER", and an
entry in table "BUSINESSRELATION". We can now utilise the BusinessRelation objects to update
the attributes of the M-N relation as we wish.
120.1.1 Background
dataNucleus-spatial allows the use of DataNucleus as persistence layer for geospatial applications
in an environment that supports the OGC SFA specification. It allows the persistence of the Java
geometry types from the JTS topology suite as well as those from the PostGIS project.
In this tutorial, we perform the basic persistence operations over spatial types using MySQL/MariaDB
and Postgis products.
package org.datanucleus.samples.spatial;
import org.postgis.Point;
The above JDO metadata has two extensions spatial-dimension and spatial-srid. These settings
specifies the format of the spatial data. SRID stands for spatial referencing system identifier and
Dimension the number of coordinates.
package org.datanucleus.samples.spatial;
import java.sql.SQLException;
import java.util.List;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;
import org.postgis.Point;
pm.makePersistentAll(sps);
pm.makePersistent(home);
tx.commit();
<persistence-unit name="MyTest">
<mapping-file>org/datanucleus/samples/jdo/spatial/package.jdo</mapping-file>
<exclude-unlisted-classes />
<properties>
<property name="javax.jdo.option.ConnectionURL" value="jdbc:mysql://127.0.0.1/nucleus"/>
<property name="javax.jdo.option.ConnectionDriverName" value="com.mysql.jdbc.Driver"/>
<property name="javax.jdo.option.ConnectionUserName" value="mysql"/>
<property name="javax.jdo.option.ConnectionPassword" value=""/>
</persistence>
End of Sample
121.1.1 Introduction
The design of an application will involve many choices, and often compromises. What is generally
accepted as good practice is to layer the application tiers and provide interfaces between these. For
example a typical web application can have 3 tiers - web tier, business-logic tier, and data-access tier.
DataNucleus provides data persistence and so, following this model, should only be present in the
data access layer. One pattern to achieve this is the data access object (DAO) pattern.
A typical DAO provides an interface that defines its contract with the outside world. This takes the
form of a series of data access and data update methods. In this tutorial we follow this and describe
how to implement a DAO using DataNucleus and JDO.
While this guide demonstrates how to write a DAO layer, it does not propose use of the DAO
pattern, just explaining how you could achieve it
So we have 3 dependent classes, and we have a 1-N relationship between Owner and Pet, and a N-1
relationship between Pet and PetType.
We now generate an outline DAO object containing the main methods that we expect to need
Clearly we could have defined more methods, but these will demonstrate the basic operations
performed in a typical application. Note that we defined our DAO as an interface. This has various
benefits, and the one we highlight here is that we can now provide an implementation using
DataNucleus and JDO. We could, in principle, provide a DAO implementation of this interface using
JDBC for example, or one for whatever persistence technology. It demonstrates a flexible design
strategy allowing components to be swapped at a future date.
We now define an outline DAO implementation using DataNucleus. We will implement just a few of
the methods defined in the interface, just to highlight the style used. So we choose one method that
retrieves data and one that stores data.
Query q = pm.newQuery(mydomain.model.Owner.class);
Collection query_owners = q.execute();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
return owners;
}
So here we've seen the typical DAO and how, for each method, we retrieve a PersistenceManager,
obtain a transaction, and perform our operation(s). Notice above there are a couple of places where
we have left "TODO" comments. These will be populated in the next section, using the JDO feature
attach/detach.
Query q = pm.newQuery(mydomain.model.Owner.class);
Collection query_owners = q.execute();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
return owners;
}
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
}
}
So we have added 2 very simple method calls. These facilitate making our objects usable outside
the DAO layer and so give us much flexibility. Please note that instead of detachCopy you could
set the PMF option "javax.jdo.option.DetachAllOnCommit" and this would silently migrate
all enlisted instances to detached state at commit of the transaction, and is probably a more
convenient way of detaching.
<fetch-group name="detach_owner_pets">
<field name="pets"/>
</fetch-group>
</class>
<class name="PetType" detachable="true">
<field name="id" primary-key="true"/>
<field name="name">
<column length="80" jdbc-type="VARCHAR"/>
</field>
</class>
<class name="Pet" detachable="true">
<field name="id" primary-key="true"/>
<field name="name">
<column length="30" jdbc-type="VARCHAR"/>
</field>
<field name="type" persistence-modifier="persistent"/>
<fetch-group name="detach_pet_type">
<field name="type"/>
</fetch-group>
</class>
Here we've marked the classes as detachable, and added a fetch-group to Owner for the "pets" field,
and to Pet for the "type" field. Doing these for each field adds extra flexibility to our ability to specify
them. Lets now update our DAO layer method using detach to use these fetch-groups so that when we
use the detached objects in our application, they have the necessary components available.
Query q = pm.newQuery(mydomain.model.Owner.class);
Collection query_owners = q.execute();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
return owners;
}
So you see that when we detach our Owner objects, we also detach the Pet objects for each owner and
for each Pet object we will also detach the PetType object. This means that we can access all of these
objects with no problem outside of the DAO layer.
121.1.5 Summary
In this tutorial we've demonstrated a way of separating the persistence code from the rest of the
application by providing a DAO layer interface. We've demonstrated how to write the associated
methods within this layer and, with the use of attach/detach and fetch-groups we can make our
persisted objects available outside of the DAO layer in a seamless way. Developers of the remainder
of the system don't need to know any details of the persistence, they simply code to the interface
contract provided by the DAO.
• The first thing to do is to mark the classes that are to be persisted as such
• JPA allows fields/properties to be defined for persistence, and you can control which of these
are persisted, and how they are persisted.
• Since JPA is oriented to RDBMS datastores only you now need to define the Object-Relational
Mapping (ORM)
Note that with DataNucleus, you can map your classes using JDO MetaData ( XML/ Annotations)
OR using JPA MetaData ( XML/ Annotations) and still use the JPA API with these classes.
At runtime the JPA code can be split into several sections.
<entity class="org.datanucleus.test.Hotel">
...
</entity>
@Entity
public class Hotel
{
...
}
In the above example we have marked the class as an entity. We could equally have marked it as
mapped-superclass (using annotation @MappedSuperclass, or XML element <mapped-superclass>)
or as embeddable (using annotation @Embeddable, or XML element <embeddable>).
See also :-
With JPA you cannot access public fields of classes. DataNucleus allows an extension to permit this,
but such classes need special enhancement. To allow this you need to
• Annotate the class that will access these public fields (assuming it isn't an Entity) with the
DataNucleus extension annotation @PersistenceAware
You perform the annotation of the class as follows
@PersistenceAware
public class MyClassThatAccessesPublicFields
{
...
}
See also :-
123.1.2 Read-Only
You can, if you wish, make a class read-only. This is a DataNucleus extension and you set it as
follows
@Entity
@Extension(vendorName="datanucleus", key="read-only", value="true")
public class MyClass
{
...
}
<entity class="org.mydomain.MyClass">
<id-class class="org.mydomain.MyIdClass"/>
<attributes>
<id name="myPrimaryKeyField"/>
</attributes>
</entity>
For JPA we specify the id field and id-class. Alternatively, if we are using annotations
@Entity
@IdClass(class=MyIdClass.class)
public class MyClass
{
@Id
private long myPrimaryKeyField;
}
When you have an inheritance hierarchy, you should specify the identity type in the base instantiable
class for the inheritance tree. This is then used for all persistent classes in the tree. This means that you
can have MappedSuperclass without any identity fields/properties as superclass, and then the base instantiable
class is the first persistable class which has the identity field(s). This is a change from DataNucleus 2.2 where
you had to have identity fields in the base persistable class of the inheritance tree.
See also :-
If you have defined your own "IdClass" then the mykey is the toString() form if the identity of your
PK class.
<entity class="MyClass">
<id-class class="MyIdClass"/>
...
</entity>
or using annotations
@Entity
@IdClass(class=MyIdClass.class)
public class MyClass
{
...
}
You now need to define the PK class to use. This is simplified for you because if you have only one
PK field then you dont need to define a PK class and you only define it when you have a composite
PK.
An important thing to note is that the PK can only be made up of fields of the following Java types
• Primitives : boolean, byte, char, int, long, short
• java.lang : Boolean, Byte, Character, Integer, Long, Short, String, Enum, StringBuffer
• java.math : BigInteger
• java.sql : Date, Time, Timestamp
• java.util : Date, Currency, Locale, TimeZone, UUID
• java.net : URI, URL
• persistable
Note that the types in bold are JPA standard types. Any others are DataNucleus extensions and, as
always, check the specific datastore docs to see what is supported for your datastore.
<entity class="MyClass">
<attributes>
<id name="id"/>
...
</attributes>
</entity>
or using annotations
@Entity
public class MyClass
{
@Id
long id;
...
}
So we didnt specify the JPA "id-class". You will, of course, have to give the field a value before
persisting the object, either by setting it yourself, or by using a value-strategy on that field.
Since there are many possible combinations of primary-key fields it is impossible for JPA to provide
a series of builtin composite primary key classes. However the DataNucleus enhancer provides a
mechanism for auto-generating a primary-key class for a persistable class. It follows the rules listed
below and should work for all cases. Obviously if you want to tailor the output of things like the PK
toString() method then you ought to define your own. The enhancer generation of primary-key class is
only enabled if you don't define your own class.
@Entity
@IdClass(ComposedIdKey.class)
public class MyClass
{
@Id
String field1;
@Id
String field2;
...
}
/**
* Default constructor.
*/
public ComposedIdKey ()
{
}
/**
* Constructor accepting same input as generated by toString().
*/
public ComposedIdKey(String value)
{
StringTokenizer token = new StringTokenizer (value, "::");
//field1
this.field1 = token.nextToken ();
//field2
this.field2 = token.nextToken ();
}
While JPA defines support for application identity only DataNucleus also provides support for
datastore identity. With datastore identity you are leaving the assignment of id's to DataNucleus
and your class will not have a field for this identity - it will be added to the datastore representation
by DataNucleus. It is, to all extents and purposes a surrogate key that will have its own column in
the datastore. To specify that a class is to use datastore identity with JPA, you define the following
annotations on your class
@Entity
@org.datanucleus.api.jpa.annotations.DatastoreIdentity
public class MyClass
{
...
}
Please note that since the JPA XML metadata is poorly designed it is not possible to specify datastore
identity using XML, you have to use the annotations.
When you have an inheritance hierarchy, you should specify the identity type in the base class for the
inheritance tree. This is then used for all persistent classes in the tree.
@Entity
@org.datanucleus.api.jpa.annotations.DatastoreIdentity(generationType=GenerationType.TABLE)
public class MyClass
{
...
}
See also :-
You should be aware however that the "identity" is in a complicated form, and is not available as a
simple integer value for example. Again, if you want an identity of that form then you should use
application identity
1[OID]mydomain.myclass
This is made up of :-
1 = identity number of this object
class-name
DataNucleus allows you the luxury of being able to provide your own datastore identity class so you
can have whatever formatting you want for identities.
or in "datanucleus-api-jpa" 3.0.7+ you can also just pass in the "key" value, like this
Object obj = em.find(MyClass.class, 101);
• 1-1 unidirectional
• 1-N collection bidirectional using ForeignKey
• 1-N map bidirectional using ForeignKey (key stored in value)
In addition we need to define primary key classes for our User and Account classes
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public User.PK user; // Use same name as the real field above
To achieve what we want with the datastore schema we define the MetaData like this
<entity-mappings>
<entity class="mydomain.User">
<table name="USER"/>
<id-class class="mydomain.User.PK"/>
<attributes>
<id name="id">
<column name="USER_ID"/>
</id>
<basic name="login">
<column name="LOGIN" length="20"/>
</basic>
</entity>
<entity class="mydomain.Account">
<table name="ACCOUNT"/>
<id-class class="mydomain.Account.PK"/>
<attributes>
<id name="user">
<column name="USER_ID"/>
</id>
<basic name="firstName">
<column name="FIRSTNAME" length="50"/>
</basic>
<basic name="secondName">
<column name="LASTNAME" length="50"/>
</basic>
<one-to-one name="user"/>
</attributes>
</entity>
</entity-mappings>
Things to note :-
• In the child Primary Key class, you must have a field with the same name as the relationship in
the child class, and the field in the child Primary Key class must be the same type as the Primary
Key class of the parent
• See also the general instructions for Primary Key classes
• You can only have one "Account" object linked to a particular "User" object since the FK to the
"User" is now the primary key of "Account". To remove this restriction you could also add a
"long id" to "Account" and make the "Account.PK" a composite primary-key
In addition we need to define primary key classes for our Account and Address classes
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
©2014, DataNucleus • ALL RIGHTS RESERVED.
{
public long id; // Same name as real field above
public Account.PK account; // Same name as the real field above
126 Compound Identity 738
To achieve what we want with the datastore schema we define the MetaData like this
<entity-mappings>
<entity class="mydomain.Account">
<table name="ACCOUNT"/>
<id-class class="mydomain.Account.PK"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
<basic name="firstName">
<column name="FIRSTNAME" length="50"/>
</basic>
<basic name="secondName">
<column name="LASTNAME" length="50"/>
</basic>
<one-to-many name="addresses" mapped-by="account"/>
</entity>
<entity class="mydomain.Address">
<table name="ADDRESS"/>
<id-class class="mydomain.Address.PK"/>
<attributes>
<id name="id">
<column name="ID"/>
</id>
<id name="account">
<column name="ACCOUNT_ID"/>
</id>
<basic name="city">
<column name="CITY"/>
</basic>
<basic name="street">
<column name="STREET"/>
</basic>
<many-to-one name="account"/>
</attributes>
</entity>
</entity-mappings>
Things to note :-
• In the child Primary Key class, you must have a field with the same name as the relationship in
the child class, and the field in the child Primary Key class must be the same type as the Primary
Key class of the parent
• See also the general instructions for Primary Key classes
• If we had omitted the "id" field from "Address" it would have only been possible to have one
"Address" in the "Account" "addresses" collection due to PK constraints. For that reason we have
the "id" field too.
In addition we need to define primary key classes for our Account and Address classes
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
©2014, DataNucleus • ALL RIGHTS RESERVED.
{
public String alias; // Same name as real field above
public Account.PK account; // Same name as the real field above
126 Compound Identity 741
To achieve what we want with the datastore schema we define the MetaData like this
<entity-mappings>
<entity class="mydomain.Account">
<table name="ACCOUNT"/>
<id-class class="mydomain.Account.PK"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
<basic name="firstName">
<column name="FIRSTNAME" length="50"/>
</basic>
<basic name="secondName">
<column name="LASTNAME" length="50"/>
</basic>
<one-to-many name="addresses" mapped-by="account">
<map-key name="alias"/>
</one-to-many>
</entity>
<entity class="mydomain.Address">
<table name="ADDRESS"/>
<id-class class="mydomain.Address.PK"/>
<attributes>
<id name="account">
<column name="ACCOUNT_ID"/>
</id>
<id name="alias">
<column name="KEY"/>
</id>
<basic name="city">
<column name="CITY"/>
</basic>
<basic name="street">
<column name="STREET"/>
</basic>
<many-to-one name="account"/>
</attributes>
</entity>
</entity-mappings>
Things to note :-
• In the child Primary Key class, you must have a field with the same name as the relationship in
the child class, and the field in the child Primary Key class must be the same type as the Primary
Key class of the parent
• See also the general instructions for Primary Key classes
• If we had omitted the "alias" field from "Address" it would have only been possible to have one
"Address" in the "Account" "addresses" collection due to PK constraints. For that reason we have
the "alias" field too as part of the PK.
127 Versioning
.......................................................................................................................................
<entity name="mydomain.User">
<attributes>
<id name="id"/>
<version name="version"/>
</attributes>
</entity>
@Entity
public class User
{
@Id
long id;
@Version
int version;
...
}
The specification above will use the "version" field for storing the version of the object. DataNucleus
will use a "version-number" strategy for populating the value.
While the above mechanism should always be used for portability, DataNucleus also supports a
surrogate version for objects of a class. With this you don't have a particular field that stores the
version and instead DataNucleus persists the version in the datastore with the field values in its own
"column" You do this as follows.
import org.datanucleus.api.jpa.annotations.SurrogateVersion;
@Entity
@SurrogateVersion
public class User
{
@Id
long id;
...
}
128 Inheritance
.......................................................................................................................................
1. The default strategy is to select a class to have its fields persisted in the table of the base class.
There is only one table per inheritance hierarchy. In JPA this is known as SINGLE_TABLE.
2. The next way is to have a table for each class in the inheritance hierarchy, and for each table to
only hold columns for the fields of that class. Fields of superclasses are persisted into the table
of the superclass. Consequently to get all field values for a subclass object a join is made of all
tables of superclasses. In JPA this is referred to as JOINED
3. The third way is like JOINED except that each table will also contain columns for all inherited
fields. In JPA this is referred to as TABLE_PER_CLASS.
In order to demonstrate the various inheritance strategies we need an example. Here are a few simple
classes representing products in a (online) store. We have an abstract base class, extending this to
provide something that we can represent any product by. We then provide a few specialisations for
typical products. We will use these classes later when defining how to persistent these objects in the
different inheritance strategies.
The default JPA strategy is "SINGLE_TABLE", namely that the base class will have a table and all
subclasses will be persisted into that same table. So if you dont specify an "inheritance strategy" in
your root class this is what you will get.
Please note that you must specify the identity of objects in the root persistable class of the inheritance
hierarchy. You cannot redefine it down the inheritance tree
See also :-
128.1.1 Discriminator
A discriminator is an extra "column" stored alongside data to identify the class of which that
information is part. It is useful when storing objects which have inheritance to provide a quick way of
determining the object type on retrieval. A discriminator in JPA will store the a specified value (or the
class name if you provide no value). You specify a discriminator as follows
<entity name="mydomain.Product">
<discriminator-column name="OBJECT" discriminator-type="STRING"/>
<discriminator-value>MyClass</discriminator-value>
...
or with annotations
@Entity
@DiscriminatorColumn(name="OBJECT_TYPE", discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("MyClass")
public class Product {...}
128.1.2 SINGLE_TABLE
Applicable to RDBMS
"SINGLE_TABLE" strategy is where the root class has a table and all subclasses are also persisted
into that table. This corresponds to JDOs "new-table" for the root class and "superclass-table" for all
subclasses. This has the advantage that retrieval of an object is a single DB call to a single table. It
also has the disadvantage that the single table can have a very large number of columns, and database
readability and performance can suffer, and additionally that a discriminator column is required.
In our example, lets ignore the AbstractProduct class for a moment and assume that Product is
the base class (with the "id"). We have no real interest in having separate tables for the Book and
CompactDisc classes and want everything stored in a single table PRODUCT. We change our
MetaData as follows
<entity name="Product">
<inheritance strategy="SINGLE_TABLE"/>
<discriminator-value>PRODUCT</discriminator-value>
<discriminator-column name="PRODUCT_TYPE" discriminator-type="STRING"/>
<attributes>
<id name="id">
<column name="PRODUCT_ID"/>
</id>
<basic name="price">
<column name="PRICE"/>
</basic>
</attributes>
</entity>
<entity name="Book">
<discriminator-value>BOOK</discriminator-value>
<attributes>
<basic name="isbn">
<column name="ISBN"/>
</basic>
<basic name="author">
<column name="AUTHOR"/>
</basic>
<basic name="title">
<column name="TITLE"/>
</basic>
</attributes>
</entity>
<entity name="TravelGuide">
<discriminator-value>TRAVELGUIDE</discriminator-value>
<attributes>
<basic name="country">
<column name="COUNTRY"/>
</basic>
</attributes>
</entity>
<entity name="CompactDisc">
<discriminator-value>COMPACTDISC</discriminator-value>
<attributes>
<basic name="artist">
<column name="ARTIST"/>
</basic>
<basic name="title">
<column name="DISCTITLE"/>
</basic>
</attributes>
</entity>
or using annotations
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Product {...}
This change of use of the inheritance element has the effect of using the PRODUCT table for all
classes, containing the fields of Product, Book, CompactDisc, and TravelGuide. You will also
note that we used a discriminator-column element for the Product class. The specification above
will result in an extra column (called PRODUCT_TYPE) being added to the PRODUCT table, and
containing the "discriminator-value" of the object stored. So for a Book it will have "BOOK" in that
column for example. This column is used in discriminating which row in the database is of which
type. The final thing to note is that in our classes Book and CompactDisc we have a field that is
identically named. With CompactDisc we have defined that its column will be called DISCTITLE
since both of these fields will be persisted into the same table and would have had identical names
otherwise - this gets around the problem.
In the above example, when we insert a TravelGuide object into the datastore, a row will be inserted
into the PRODUCT table only.
128.1.3 JOINED
Applicable to RDBMS
"JOINED" strategy means that each entity in the inheritance hierarchy has its own table and that
the table of each class only contains columns for that class. Inherited fields are persisted into the
tables of the superclass(es). This corresponds to JDO2s "new-table" (for all classes in the inheritance
hierarchy). This has the advantage of being the most normalised data definition. It also has the
disadvantage of being slower in performance since multiple tables will need to be accessed to retrieve
an object of a sub-type. Let's try an example using the simplest to understand strategy JOINED. We
have the classes defined above, and we want to persist our classes each in their own table. We define
the Meta-Data for our classes like this
<entity class="AbstractProduct">
<inheritance strategy="JOINED"/>
<attributes>
<id name="id">
<column name="PRODUCT_ID"/>
</id>
<basic name="name">
<column name="NAME"/>
</basic>
<basic name="description">
<column name="DESCRIPTION"/>
</basic>
</attributes>
</entity>
<entity class="Product">
<attributes>
<basic name="price">
<column name="PRICE"/>
</basic>
</attributes>
</entity>
<entity class="Book">
<attributes>
<basic name="isbn">
<column name="ISBN"/>
</basic>
<basic name="author">
<column name="AUTHOR"/>
</basic>
<basic name="title">
<column name="TITLE"/>
</basic>
</attributes>
</entity>
<entity class="TravelGuide">
<attributes>
<basic name="country">
<column name="COUNTRY"/>
</basic>
</attributes>
</entity>
<entity class="CompactDisc">
<attributes>
<basic name="artist">
<column name="ARTIST"/>
</basic>
<basic name="title">
<column name="TITLE"/>
</basic>
</attributes>
</entity>
or using annotations
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Product {...}
In the above example, when we insert a TravelGuide object into the datastore, a row will be inserted
into ABSTRACTPRODUCT, PRODUCT, BOOK, and TRAVELGUIDE.
128.1.4 TABLE_PER_CLASS
This strategy is like "JOINED" except that in addition to each class having its own table, the table also
holds columns for all inherited fields. So taking the same classes as used above
<entity class="AbstractProduct">
<inheritance strategy="TABLE_PER_CLASS"/>
<attributes>
<id name="id">
<column name="PRODUCT_ID"/>
</id>
<basic name="name">
<column name="NAME"/>
</basic>
<basic name="description">
<column name="DESCRIPTION"/>
</basic>
</attributes>
</entity>
<entity class="Product">
<attributes>
<basic name="price">
<column name="PRICE"/>
</basic>
</attributes>
</entity>
<entity class="Book">
<attributes>
<basic name="isbn">
<column name="ISBN"/>
</basic>
<basic name="author">
<column name="AUTHOR"/>
</basic>
<basic name="title">
<column name="TITLE"/>
</basic>
</attributes>
</entity>
<entity class="TravelGuide">
<attributes>
<basic name="country">
<column name="COUNTRY"/>
</basic>
</attributes>
</entity>
<entity class="CompactDisc">
<attributes>
<basic name="artist">
<column name="ARTIST"/>
</basic>
<basic name="title">
<column name="TITLE"/>
</basic>
</attributes>
</entity>
or using annotations
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Product {...}
So any object of explicit type Book is persisted into the table BOOK. Similarly any TravelGuide is
persisted into the table TRAVELGUIDE, etc In addition if any class in the inheritance tree is abstract
then it won't have a table since there cannot be any instances of that type. DataNucleus currently has
limitations when using a class using this inheritance as the element of a collection.
<mapped-superclass class="AbstractProduct">
<attributes>
<id name="id">
<column name="PRODUCT_ID"/>
</id>
<basic name="name">
<column name="NAME"/>
</basic>
<basic name="description">
<column name="DESCRIPTION"/>
</basic>
</attributes>
</mapped-superclass>
<entity class="Product">
<attributes>
<basic name="price">
<column name="PRICE"/>
</basic>
</attributes>
</entity>
In this case we will have a table for Product and the fields of AbstractProduct will be stored in this
table. If the mapping information (column names etc) for these fields need setting then you should use
<attribute-override> in the MetaData for Product.
129 Fields/Properties
.......................................................................................................................................
@Entity
public class MyClass
{
@Basic
Date birthday;
@Transient
String someOtherField;
}
So, using annotations, we have marked this class as persistent, and the field birthday also as
persistent, whereas field someOtherField is not persisted. Using XML MetaData we would have done
<entity name="mydomain.MyClass">
<attributes>
<basic name="birthday"/>
<transient name="someOtherField"/>
</attributes>
</entity>
Please note that the field Java type defines whether it is, by default, persistable. Look at the
Types Guide and if the type has a tick in the column "Persistent?" then you can omit the "basic"
specification.
@Entity
public class MyClass
{
@Basic
Date getBirthday()
{
...
}
So, using annotations, we have marked this class as persistent, and the getter is marked as persistent.
By default a property is non-persistent, so we have no need in specifying the someOtherField as
transient. Using XML MetaData we would have done
<entity name="mydomain.MyClass">
<attributes>
<basic name="birthday"/>
</attributes>
</entity>
With some datastores (notably spreadsheets) it is desirable to be able to specify the relative position
of a column. The default (for DataNucleus) is just to put them in ascending alphabetical order. JPA
doesn't allow configuration of this, but DataNucleus provides the following vendor extension. It is
currently only possible using (DataNucleus) annotations
@Entity
@Table(name="People")
public class Person
{
@Id
@ColumnPosition(0)
long personNum;
@ColumnPosition(1)
String firstName;
@ColumnPosition(2)
String lastName;
}
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 datanucleus-
[4] core
java.lang.Object datanucleus-
core
java.lang.String datanucleus-
core
java.lang.StringBuffer datanucleus-
[3] 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 datanucleus-
[7] core
java.util.HashMap datanucleus-
core
java.util.HashSet datanucleus-
core
java.util.Hashtable datanucleus-
core
java.util.LinkedHashMap datanucleus-
[5] core
java.util.LinkedHashSet datanucleus-
[6] 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 datanucleus-
[2] core
java.util.SortedSet datanucleus-
[1] core
java.util.Stack datanucleus-
core
java.util.TimeZone datanucleus-
core
java.util.TreeMap datanucleus-
[2] core
java.util.TreeSet datanucleus-
[1] 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
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
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
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;
• AUTO - this is the default and allows DataNucleus to choose the most suitable for the datastore
• SEQUENCE - this uses a datastore sequence (if supported by the datastore)
• IDENTITY - these use autoincrement/identity/serial features in the datastore (if supported by
the datastore)
• TABLE - this is datastore neutral and increments a sequence value using a table.
See also :-
Please note that by defining a value-strategy for a field then it will, by default, always
generate a value for that field on persist. If the field can store nulls and you only want it to
generate the value at persist when it is null (i.e you haven't assigned a value yourself) then
you can add the extension "strategy-when-notnull" as false
131.1.1 AUTO
With this strategy DataNucleus will choose the most appropriate strategy for the datastore being used.
If you define the field as String-based then it will choose uuid-hex. Otherwise the field is numeric in
which case it chooses identity if supported, otherwise sequence if supported, otherwise increment
if supported otherwise throws an exception. On RDBMS you can get the behaviour used up until DN
v3.0 by specifying the persistence property datanucleus.rdbms.useLegacyNativeValueStrategy as
true For a class using application identity you need to set the value-strategy attribute on the primary
key field. You can configure the Meta-Data for the class something like this
<entity class="MyClass">
<attributes>
<id name="myId">
<generated-value strategy="AUTO"/>
</id>
</attributes>
</entity>
or using annotations
@Entity
public class MyClass
{
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long myId;
...
}
131.1.2 SEQUENCE
A sequence is a user-defined database function that generates a sequence of unique numeric ids.
The unique identifier value returned from the database is translated to a java type: java.lang.Long.
DataNucleus supports sequences for the following datastores:
• Oracle
• PostgreSQL
• SAP DB
• DB2
• Firebird
• HSQLDB
• H2
• Derby (from v10.6)
• SQLServer (from v2012)
• NuoDB
To configure a class to use either of these generation methods using application identity you would
add the following to the class' Meta-Data
or using annotations
@Entity
@SequenceGenerator(name="SEQ1", sequenceName="MY_SEQ", initialValue=5, allocationSize=10)
public class MyClass
{
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ1")
private long myId;
...
}
If the sequence does not yet exist in the database at the time DataNucleus needs a new unique
identifier, a new sequence is created in the database based on the JPA Meta-Data configuration.
Additional properties for configuring sequences are set in the JPA Meta-Data, see the available
properties below. Unsupported properties by a database are silently ignored by DataNucleus.
This value generator will generate values unique across different JVMs
131.1.3 IDENTITY
Auto-increment/identity/serial are primary key columns that are populated when a row is inserted
in the table. These use the databases own keywords on table creation and so rely on having the table
structure either created by DataNucleus or having the column with the necessary keyword.
DataNucleus supports auto-increment/identity/serial keys for many databases including :
• DB2 (IDENTITY)
• MySQL (AUTOINCREMENT)
• MSSQL (IDENTITY)
• Sybase (IDENTITY)
• HSQLDB (IDENTITY)
• H2 (IDENTITY)
• PostgreSQL (SERIAL)
• Derby (IDENTITY)
• MongoDB - String based
• Neo4j - long based
• NuoDB (IDENTITY)
This generation strategy should only be used if there is a single "root" table for the inheritance
tree. If you have more than 1 root table (e.g using subclass-table inheritance) then you should
choose a different generation strategy
For a class using application identity you need to set the value-strategy attribute on the primary key
field. You can configure the Meta-Data for the class something like this
<entity class="MyClass">
<attributes>
<id name="myId">
<generated-value strategy="IDENTITY"/>
</id>
</attributes>
</entity>
or using annotations
@Entity
public class MyClass
{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private long myId;
...
}
Please be aware that if you have an inheritance tree with the base class defined as using "identity"
then the column definition for the PK of the base table will be defined as "AUTO_INCREMENT"
or "IDENTITY" or "SERIAL" (dependent on the RDBMS) and all subtables will NOT have this
identifier added to their PK column definitions. This is because the identities are assigned in the base
table (since all objects will have an entry in the base table).
Please note that if using optimistic transactions, this strategy will mean that the value is only set
when the object is actually persisted (i.e at flush() or commit())
This value generator will generate values unique across different JVMs
131.1.4 TABLE
This method is database neutral and uses a sequence table that holds an incrementing sequence value.
The unique identifier value returned from the database is translated to a java type: java.lang.Long.
This strategy will work with any datastore. This method require a sequence table in the database and
creates one if doesn't exist.
To configure an application identity class to use this generation method you simply add this to the
class' Meta-Data. If your class is in an inheritance tree you should define this for the base class only.
<entity class="MyClass">
<attributes>
<id name="myId">
<generated-value strategy="TABLE"/>
</id>
</attributes>
</entity>
or using annotations
@Entity
public class MyClass
{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
private long myId;
...
}
Additional properties for configuring this generator are set in the JPA Meta-Data, see the available
properties below. Unsupported properties are silently ignored by DataNucleus.
This value generator will generate values unique across different JVMs
<embeddable name="mydomain.MyClass">
...
</embeddable>
or using annotations
@Embeddable
public class MyClass
{
...
}
With the above MetaData (using the embeddable definition), in our application any objects of the
class MyClass can be embedded into other objects.
JPA's definition of embedding encompasses several types of fields. These are described below
• Embedded Entities - where you have a 1-1 relationship and you want to embed the other Entity
into the same table as the your object.
• Embedded Nested Entities - like the first example except that the other object also has another
Entity that also should be embedded
• Embedded Collection elements - where you want to embed the elements of a collection into a
join table (instead of persisting them into their own table)
Applicable to RDBMS, Excel, OOXML, ODF, HBase, MongoDB, Neo4j, Cassandra, JSON.
In a typical 1-1 relationship between 2 classes, the 2 classes in the relationship are persisted to their
own table, and a foreign key is managed between them. With JPA and DataNucleus you can persist
the related entity object as embedded into the same table. This results in a single table in the datastore
rather than one for each of the 2 classes.
Let's take an example. We are modelling a Computer, and in our simple model our Computer has
a graphics card and a sound card. So we model these cards using a ComputerCard class. So our
classes become
...
}
...
}
The traditional (default) way of persisting these classes would be to have a table to represent each
class. So our datastore will look like this
However we decide that we want to persist Computer objects into a table called COMPUTER and
we also want to persist the PC cards into the same table. We define our MetaData like this
<entity name="mydomain.Computer">
<attributes>
<basic name="operatingSystem">
<column="OS_NAME"/>
</basic>
<embedded name="graphicsCard">
<attribute-override name="manufacturer">
<column="GRAPHICS_MANUFACTURER"/>
</attribute-override>
<attribute-override name="type">
<column="GRAPHICS_TYPE"/>
</attribute-override>
</embedded>
<embedded name="soundCard">
<attribute-override name="manufacturer">
<column="SOUND_MANUFACTURER"/>
</attribute-override>
<attribute-override name="type">
<column="SOUND_TYPE"/>
</attribute-override>
</embedded>
</attributes>
</entity>
<embeddable name="mydomain.ComputerCard">
<attributes>
<basic name="manufacturer"/>
<basic name="type"/>
</attributes>
</embeddable>
It should be noted that in this latter (embedded) case we can still persist objects of type
ComputerCard into their own table - the MetaData definition for ComputerCard is used for the
table definition in this case.
DataNucleus supports embedded persistable objects with the following proviso :-
• You can represent inheritence of embedded objects using a discriminator (you must define it in
the metadata of the embedded type. Note that this is a DataNucleus extension since JPA doesn't
define any support for embedded inherited persistable objects
See also :-
Applicable to RDBMS, Excel, OOXML, ODF, HBase, MongoDB, Neo4j, Cassandra, JSON.
In the above example we had an embedded Entity within a persisted object. What if our embedded
Persistable object also contain another Entity object. So, using the above example what if
ComputerCard contains an object of type Connector ?
@Embeddable
public class ComputerCard
{
...
@Embedded
Connector connector;
...
}
@Embeddable
public class Connector
{
int type;
}
Well we want to store all of these objects into the same record in the COMPUTER table.
<entity name="mydomain.Computer">
<attributes>
<basic name="operatingSystem">
<column="OS_NAME"/>
</basic>
<embedded name="graphicsCard">
<attribute-override name="manufacturer">
<column="GRAPHICS_MANUFACTURER"/>
</attribute-override>
<attribute-override name="type">
<column="GRAPHICS_TYPE"/>
</attribute-override>
<attribute-override name="connector.type">
<column="GRAPHICS_CONNECTOR_TYPE"/>
</attribute-override>
</embedded>
<embedded name="soundCard">
<attribute-override name="manufacturer">
<column="SOUND_MANUFACTURER"/>
</attribute-override>
<attribute-override name="type">
<column="SOUND_TYPE"/>
</attribute-override>
<attribute-override name="connector.type">
<column="SOUND_CONNECTOR_TYPE"/>
</attribute-override>
</embedded>
</attributes>
</entity>
<embeddable name="mydomain.ComputerCard">
<attributes>
<basic name="manufacturer"/>
<basic name="type"/>
</attributes>
</embeddable>
<embeddable name="mydomain.Connector">
<attributes>
<basic name="type"/>
</attributes>
</embeddable>
So we simply nest the embedded definition of the Connector objects within the embedded definition
of the ComputerCard definitions for Computer. JPA supports this to as many levels as you
require! The Connector objects will be persisted into the GRAPHICS_CONNECTOR_TYPE, and
SOUND_CONNECTOR_TYPE columns in the COMPUTER table.
In a typical 1-N relationship between 2 classes, the 2 classes in the relationship are persisted to their
own table, and either a join table or a foreign key is used to relate them. With JPA and DataNucleus
you have a variation on the join table relation where you can persist the objects of the "N" side into
the join table itself so that they don't have their own identity, and aren't stored in the table for that
class. This is supported in DataNucleus with the following provisos
• You can have inheritance in embedded keys/values and a discriminator is added (you must
define the discriminator in the metadata of the embedded type).
• When retrieving embedded elements, all fields are retrieved in one call. That is, fetch plans are
not utilised. This is because the embedded element has no identity so we have to retrieve all
initially.
It should be noted that where the collection "element" is not Persistable or of a "reference" type
(Interface or Object) it will always be embedded, and this functionality here applies to Persistable
elements only. DataNucleus doesn't support the embedding of reference type objects currently.
Let's take an example. We are modelling a Network, and in our simple model our Network has
collection of Devices. So we define our classes as
@Entity
public class Network
{
private String name;
@Embedded
@ElementCollection
private Collection<Device> devices = new HashSet();
...
}
@Embeddable
public class Device
{
private String name;
...
}
We decide that instead of Device having its own table, we want to persist them into the join table
of its relationship with the Network since they are only used by the network itself. We define our
MetaData like this
<entity name="mydomain.Network">
<attributes>
<basic name="name">
<column="NAME" length="40"/>
</basic>
<element-collection name="devices">
<collection-table name="NETWORK_DEVICES">
<join-column name="NETWORK_ID"/>
</collection-table>
</element-collection>
</attributes>
</entity>
<embeddable name="mydomain.Device">
<attributes>
<basic name="name">
<column="DEVICE_NAME"/>
</basic>
<basic name="ipAddress">
<column="DEVICE_IP_ADDR"/>
</basic>
</attributes>
</embeddable>
So here we will end up with a table called "NETWORK" with columns "NETWORK_ID",
and "NAME", and a table called "NETWORK_DEVICES" with columns "NETWORK_ID",
"ADPT_PK_IDX", "DEVICE_NAME", "DEVICE_IP_ADDR". When we persist a Network object,
any devices are persisted into the NETWORK_DEVICES table.
See also :-
If you wish to serialise a particular field into a single column (in the table of the class), you need to
simply mark the field as a "lob" (large object). Let's take an example. We have the following classes
and we want the animals collection to be serialised into a single column in the table storing the Farm
class, so we define our MetaData like this
<entity class="Farm">
<table name="FARM"/>
<attributes>
...
<basic name="animals">
<column name="ANIMALS"/>
<lob/>
</basic>
...
</attributes>
</entity>
So we make use of the lob element (or @Lob annotation). This specification results in a table like this
Applicable to RDBMS
Note this is not part of the JPA spec, but is available in DataNucleus to ease your usage. If you
have a non-relation field that implements Serializable you have the option of serialising it into a file
on the local disk. This could be useful where you have a large file and don't want to persist very large
objects into your RDBMS. Obviously this will mean that the field is no longer queryable, but then if
its a large file you likely don't care about that. So let's give an example
@Entity
public class Person
{
@Id
long id;
@Basic
@Lob
@Extension(vendorName="datanucleus", key="serializeToFileLocation"
value="person_avatars")
AvatarImage image;
}
Or using XML
<entity class="Person">
<attributes>
...
<basic name="image">
<lob/>
<extension key="serializeToFileLocation" value="person_avatars"
</basic>
...
</attributes>
</entity>
So this will now persist a file into a folder person_avatars with filename as the String form of the
identity of the owning object. In a real world example you likely will specify the extension value as an
absolute path name, so you can place it anywhere in the local disk.
JPA doesn't define support for persisting fields of type interface, but DataNucleus provides an
extension whereby the implementations of the interface are persistable objects. It follows the same
general process as for java.lang.Object since both interfaces and java.lang.Object are basically
references to some persistable object.
To demonstrate interface handling let's introduce some classes. Let's suppose you have an interface
with a selection of classes implementing the interface something like this
You then have a class that contains an object of this interface type
The user controls which one of these is to be used by specifying the extension mapping-strategy on
the field containing the interface. The default is "per-implementation"
In terms of the implementations of the interface, you can either leave the field to accept any
known about implementation, or you can restrict it to only accept some implementations (see
"implementation-classes" metadata extension). If you are leaving it to accept any persistable
implementation class, then you need to be careful that such implementations are known to
DataNucleus at the point of encountering the interface field. By this we mean, DataNucleus has to
have encountered the metadata for the implementation so that it can allow for the implementation
when handling the field. You can force DataNucleus to know about a persistable class by using an
autostart mechanism, or using persistence.xml, or by placement of the package.jdo file so that when
the owning class for the interface field is encountered so is the metadata for the implementations.
134.1.1 1-1
To allow persistence of this interface field with DataNucleus you have 2 levels of control. The first
level is global control. Since all of our Square, Circle, Rectangle classes implement Shape then we
just define them in the MetaData as we would normally.
The global way means that when mapping that field DataNucleus will look at all Entities it knows
about that implement the specified interface.
DataNucleus also allows users to specify a list of classes implementing the interface on a field-by-
field basis, defining which of these implementations are accepted for a particular interface field. To do
this you define the Meta-Data like this
@Entity
public class ShapeHolder
{
@OneToOne
@Extension(key="implementation-classes",
value="mydomain.Circle,mydomain.Rectangle,mydomain.Square")
Shape shape;
...
}
That is, for any interface object in a class to be persisted, you define the possible implementation
classes that can be stored there. DataNucleus interprets this information and will map the above
example classes to the following in the database
So DataNucleus adds foreign keys from the containers table to all of the possible implementation
tables for the shape field.
If we use mapping-strategy of "identity" then we get a different datastore schema.
@Entity
public class ShapeHolder
{
@OneToOne
@Extensions(
@Extension(key="implementation-classes",
value="mydomain.Circle,mydomain.Rectangle,mydomain.Square"),
@Extension(key="mapping-strategy", value="identity")})
Shape shape;
...
}
and the column "SHAPE" will contain strings such as mydomain.Circle:1 allowing retrieval of the
related implementation object.
134.1.2 1-N
You can have a Collection/Map containing elements of an interface type. You specify this in the same
way as you would any Collection/Map. You can have a Collection of interfaces as long as you use
a join table relation and it is unidirectional. The "unidirectional" restriction is that the interface
is not persistent on its own and so cannot store the reference back to the owner object. Use the 1-N
relationship guides for the metadata definition to use.
You need to use a DataNucleus extension tag "implementation-classes" if you want to restrict the
collection to only contain particular implementations of an interface. For example
@Entity
public class ShapeHolder
{
@OneToMany
@JoinTable
@Extensions(
@Extension(key="implementation-classes",
value="mydomain.Circle,mydomain.Rectangle,mydomain.Square"),
@Extension(key="mapping-strategy", value="identity")})
Collection<Shape> shapes;
...
}
So the shapes field is a Collection of mydomain.Shape and it will accept the implementations of type
Circle, Rectangle, Square and Triangle. If you omit the implementation-classes tag then you have
to give DataNucleus a way of finding the metadata for the implementations prior to encountering this
field.
JPA doesn't specify support for persisting fields of type java.lang.Object, however DataNucleus does
support this where the values of that field are persistable objects themselves. This follows the same
general process as for Interfaces since both interfaces and java.lang.Object are basically references to
some persistable object.
java.lang.Object cannot be used to persist non-persistable types with fixed schema datastore (e.g
RDBMS). Think of how you would expect it to be stored if you think it ought to
DataNucleus allows the following ways of persisting Object fields :-
So we have a space in a car park, and in that space we have an occupier of the space. We have
some legacy data and so can't make the type of this "occupier" an interface type, so we just use
java.lang.Object. Now we know that we can only have particular types of objects stored there (since
there are only a few types of vehicle that can enter the car park). So we define our MetaData like this
@OneToOne
@Extension(key="implementation-classes",
value="mydomain.samples.vehicles.Car,mydomain.samples.vehicles.Motorbike")
Object occupier;
}
So DataNucleus adds foreign keys from the ParkingSpace table to all of the possible implementation
tables for the occupier field.
In conclusion, when using "per-implementation" mapping for any java.lang.Object field in a class to
be persisted (as non-serialised), you must define the possible "implementation" classes that can be
stored there.
If we use mapping-strategy of "identity" then we get a different datastore schema.
@OneToOne
@Extensions({
@Extension(key="implementation-classes",
value="mydomain.samples.vehicles.Car,mydomain.samples.vehicles.Motorbike"),
@Extension(key="mapping-strategy", value="identity")})
Object occupier;
}
Please refer to the serialised fields guide for more details of storing objects in this way.
So we have an Account and it has a number of permissions, each expressed as a byte. We want
to persist the permissions in a single-column into the table of the account (but we don't want them
serialised). We then define MetaData something like this
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
...
<basic name="permissions">
<column name="PERMISSIONS"/>
<lob/>
</basic>
...
</attributes>
</entity>
See also :-
• MetaData reference for <basic> element
137.1.1 Unidirectional
For this case you could have 2 classes, User and Account, as below.
so the Account class knows about the User class, but not vice-versa. If you define the XML metadata
for these classes as follows
<entity-mappings>
<entity class="User">
<table name="USER"/>
<attributes>
<id name="id">
<column name="USER_ID"/>
</id>
...
</entity>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-one name="user">
<join-column name="USER_ID"/>
</one-to-one>
</attributes>
</entity>
</entity-mappings>
@OneToOne
@JoinColumn(name="USER_ID")
User user;
}
This will create 2 tables in the database, one for User (with name USER), and one for Account (with
name ACCOUNT and a column USER_ID), as shown below.
Things to note :-
• Account has the object reference to User (and so is the "owner" of the relation) and so its table
holds the foreign-key
• If you call EntityManager.remove() on the end of a 1-1 unidirectional relation without the
relation and that object is related to another object, an exception will typically be thrown
(assuming the RDBMS supports foreign keys). To delete this record you should remove the other
objects association first.
137.1.2 Bidirectional
For this case you could have 2 classes, User and Account again, but this time as below. Here the
Account class knows about the User class, and also vice-versa.
We create the 1-1 relationship with a single foreign-key. To do this you define the XML metadata as
<entity-mappings>
<entity class="User">
<table name="USER"/>
<attributes>
<id name="id">
<column name="USER_ID"/>
</id>
...
<one-to-one name="account" mapped-by="user"/>
</attributes>
</entity>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-one name="user">
<join-column name="USER_ID"/>
</one-to-one>
</attributes>
</entity>
</entity-mappings>
@OneToOne
@JoinColumn(name="USER_ID")
User user;
}
@OneToOne(mappedBy="user")
Account account;
...
}
The difference is that we added mapped-by to the field of User making it bidirectional (and putting the FK at the
other side for RDBMS)
This will create 2 tables in the database, one for User (with name USER), and one for Account (with
name ACCOUNT). For RDBMS it includes a USER_ID column in the ACCOUNT table, like this
For other types of datastore it will have a USER_ID column in the ACCOUNT table and a ACCOUNT
column in the USER table.
Things to note :-
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
139 Collections
.......................................................................................................................................
There are 2 ways that we can persist this relationship. These are shown below
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address">
<join-table name="ACCOUNT_ADDRESSES">
<join-column name="ACCOUNT_ID_OID"/>
<inverse-join-column name="ADDRESS_ID_EID"/>
</join-table>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
</attributes>
</entity>
</entity-mappings>
@OneToMany
@JoinTable(name="ACCOUNT_ADDRESSES")
@JoinColumn(name="ACCOUNT_ID_OID")
@InverseJoinColumn(name="ADDRESS_ID_EID")
Collection<Address> addresses
}
The crucial part is the join-table element on the field element - this signals to JPA to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• To specify the name of the join table, specify the join-table element below the one-to-many
element with the collection.
• To specify the names of the join table columns, use the join-column and inverse-join-column
elements below the join-table element.
• The join table will NOT be given a primary key (since a Collection can have duplicates).
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address">
<join-column name="ACCOUNT_ID"/>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
</attributes>
</entity>
</entity-mappings>
@OneToMany
@JoinColumn(name="ACCOUNT_ID")
Collection<Address> addresses
}
Note that you MUST specify the join-column here otherwise it defaults to a join table with JPA!
There will be 2 tables, one for Address, and one for Account. If you wish to specify the names of the
column(s) used in the schema for the foreign key in the Address table you should use the join-column
element within the field of the collection.
In terms of operation within your classes of assigning the objects in the relationship. You have to take
your Account object and add the Address to the Account collection field since the Address knows
nothing about the Account.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this
mode of persistence will not allow duplicate values in the Collection. If you want to allow duplicate
Collection entries, then use the "Join Table" variant above.
There are 2 ways that we can persist this relationship. These are shown below
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address" mapped-by="account">
<join-table name="ACCOUNT_ADDRESSES">
<join-column name="ACCOUNT_ID_OID"/>
<inverse-join-column name="ADDRESS_ID_EID"/>
</join-table>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
<many-to-one name="account"/>
</attributes>
</entity>
</entity-mappings>
@OneToMany(mappedBy="account")
@JoinTable(name="ACCOUNT_ADDRESSES")
@JoinColumn(name="ACCOUNT_ID_OID")
@InverseJoinColumn(name="ADDRESS_ID_EID")
Collection<Address> addresses
}
@ManyToOne
Account account;
...
}
The crucial part is the join element on the field element - this signals to JPA to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• To specify the name of the join table, specify the join-table element below the one-to-many
element with the collection.
• To specify the names of the join table columns, use the join-column and inverse-join-column
elements below the join-table element.
• The join table will NOT be given a primary key (since a Collection can have duplicates).
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address" mapped-by="account">
<join-column name="ACCOUNT_ID"/>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
<many-to-one name="account"/>
</attributes>
</entity>
</entity-mappings>
@OneToMany(mappedBy="account")
@JoinColumn(name="ACCOUNT_ID")
Collection<Address> addresses
}
@ManyToOne
Account account;
...
}
The crucial part is the mapped-by attribute of the field on the "1" side of the relationship. This tells the JPA
implementation to look for a field called account on the Address class.
This will create 2 tables in the database, one for Address (including an ACCOUNT_ID to link to the
ACCOUNT table), and one for Account. Notice the subtle difference to this set-up to that of the Join
Table relationship earlier.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the
classelement
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this
mode of persistence will not allow duplicate values in the Collection. If you want to allow duplicate
Collection entries, then use the "Join Table" variant above.
In JPA1 you cannot have a 1-N collection of non-Entity objects. All of the examples above show a 1-
N relationship between 2 persistable classes. If you want the element to be primitive or Object types
then follow this section. For example, when you have a Collection of Strings. This will be persisted
in the same way as the "Join Table" examples above. A join table is created to hold the collection
elements. Let's take our example. We have an Account that stores a Collection of addresses. These
addresses are simply Strings. We define the annotations like this
@Entity
public class Account
{
...
@ElementCollection
@CollectionTable(name="ACCOUNT_ADDRESSES")
Collection<String> addresses;
}
<entity class="mydomain.Account">
<attributes>
...
<element-collection name="addresses">
<collection-table name="ACCOUNT_ADDRESSES"/>
</element-collection>
</attributes>
</entity>
The ACCOUNT table is as before, but this time we only have the "join table". Use @Column on the
field/method to define the column details of the element in the join table.
140 Sets
.......................................................................................................................................
There are 2 ways that we can persist this relationship. These are shown below
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address">
<join-table name="ACCOUNT_ADDRESSES">
<join-column name="ACCOUNT_ID_OID"/>
<inverse-join-column name="ADDRESS_ID_EID"/>
</join-table>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
</attributes>
</entity>
</entity-mappings>
@OneToMany
@JoinTable(name="ACCOUNT_ADDRESSES")
@JoinColumn(name="ACCOUNT_ID_OID")
@InverseJoinColumn(name="ADDRESS_ID_EID")
Set<Address> addresses
}
The crucial part is the join-table element on the field element - this signals to JPA to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• To specify the name of the join table, specify the join-table element below the one-to-many
element with the Set
• To specify the names of the join table columns, use the join-column and inverse-join-column
elements below the join-table element.
• The join table will be given a primary key (since a Set can't have duplicates).
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address">
<join-column name="ACCOUNT_ID"/>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
</attributes>
</entity>
</entity-mappings>
@OneToMany
@JoinColumn(name="ACCOUNT_ID")
Set<Address> addresses
}
Note that you MUST specify the join-column here otherwise it defaults to a join table with JPA!
There will be 2 tables, one for Address, and one for Account. If you wish to specify the names of the
column(s) used in the schema for the foreign key in the Address table you should use the join-column
element within the field of the Set.
In terms of operation within your classes of assigning the objects in the relationship. You have to take
your Account object and add the Address to the Account Set field since the Address knows nothing
about the Account.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this
mode of persistence will not allow duplicate values in the Set. If you want to allow duplicate Set
entries, then use the "Join Table" variant above.
There are 2 ways that we can persist this relationship. These are shown below
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address" mapped-by="account">
<join-table name="ACCOUNT_ADDRESSES">
<join-column name="ACCOUNT_ID_OID"/>
<inverse-join-column name="ADDRESS_ID_EID"/>
</join-table>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
<many-to-one name="account">
</many-to-one>
</attributes>
</entity>
</entity-mappings>
@OneToMany(mappedBy="account")
@JoinTable(name="ACCOUNT_ADDRESSES")
@JoinColumn(name="ACCOUNT_ID_OID")
@InverseJoinColumn(name="ADDRESS_ID_EID")
Set<Address> addresses
}
@ManyToOne
Account account;
...
}
The crucial part is the join element on the field element - this signals to JPA to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• To specify the name of the join table, specify the join-table element below the one-to-many
element with the set.
• To specify the names of the join table columns, use the join-column and inverse-join-column
elements below the join-table element.
• The join table will be given a primary key (since a Set can't have duplicates).
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address" mapped-by="account">
<join-column name="ACCOUNT_ID"/>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
<many-to-one name="account">
</many-to-one>
</attributes>
</entity>
</entity-mappings>
@OneToMany(mappedBy="account")
@JoinColumn(name="ACCOUNT_ID")
Set<Address> addresses
}
@ManyToOne
Account account;
...
}
The crucial part is the mapped-by attribute of the field on the "1" side of the relationship. This tells the JPA
implementation to look for a field called account on the Address class.
This will create 2 tables in the database, one for Address (including an ACCOUNT_ID to link to the
ACCOUNT table), and one for Account. Notice the subtle difference to this set-up to that of the Join
Table relationship earlier.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the
classelement
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this
mode of persistence will not allow duplicate values in the Set. If you want to allow duplicate Set
entries, then use the "Join Table" variant above.
In JPA1 you cannot have a 1-N set of non-Entity objects. This is available in JPA2. All of the
examples above show a 1-N relationship between 2 persistable classes. If you want the element to
be primitive or Object types then follow this section. For example, when you have a Set of Strings.
This will be persisted in the same way as the "Join Table" examples above. A join table is created to
hold the collection elements. Let's take our example. We have an Account that stores a Collection of
addresses. These addresses are simply Strings. We define the annotations like this
@Entity
public class Account
{
...
@ElementCollection
@CollectionTable(name="ACCOUNT_ADDRESSES")
Collection<String> addresses;
}
<entity class="mydomain.Account">
<attributes>
...
<element-collection name="addresses">
<collection-table name="ACCOUNT_ADDRESSES"/>
</element-collection>
</attributes>
</entity>
The ACCOUNT table is as before, but this time we only have the "join table". Use @Column on the
field/method to define the column details of the element in the join table.
141 Lists
.......................................................................................................................................
There are 2 ways that we can persist this relationship. These are shown below
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address">
<order-by>id</order-by>
<join-table name="ACCOUNT_ADDRESSES">
<join-column name="ACCOUNT_ID_OID"/>
<inverse-join-column name="ADDRESS_ID_EID"/>
</join-table>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
</attributes>
</entity>
</entity-mappings>
@OneToMany
@OrderBy("id")
@JoinTable(name="ACCOUNT_ADDRESSES")
@JoinColumn(name="ACCOUNT_ID_OID")
@InverseJoinColumn(name="ADDRESS_ID_EID")
List<Address> addresses
}
The crucial part is the join-table element on the field element - this signals to JPA to use a join table.
There will be 3 tables, one for Address, one for Account, and the join table. This is identical to the
handling for Sets/Collections. Note that we specified <order-by> which defines the order the elements
are retrieved in (the "id" is the field in the List element).
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• To specify the name of the join table, specify the join-table element below the one-to-many
element with the collection.
• To specify the names of the join table columns, use the join-column and inverse-join-column
elements below the join-table element.
• The join table will NOT be given a primary key (so that duplicates can be stored).
• If you want to have a surrogate column added to contain the ordering you should specify order-
column (@OrderColumn) instead of order-by. This is available from JPA2
DataNucleus has to use columns in the datastore representation of the Address class. So we define the
XML metadata like this
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address">
<order-by>city</order-by>
<join-column name="ACCOUNT_ID"/>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
</attributes>
</entity>
</entity-mappings>
@OneToMany
@OrderBy("city")
@JoinColumn(name="ACCOUNT_ID")
List<Address> addresses
}
Note that you MUST specify the join-column here otherwise it defaults to a join table with JPA!
Again there will be 2 tables, one for Address, and one for Account. Note that we have no "mapped-
by" attribute specified, and also no "join" element. If you wish to specify the names of the columns
used in the schema for the foreign key in the Address table you should use the element element within
the field of the collection.
In terms of operation within your classes of assigning the objects in the relationship. With
DataNucleus and List-based containers you have to take your Account object and add the Address to
the Account collection field since the Address knows nothing about the Account.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
Limitations
• If we are using an "ordered List" and the primary key of the join table contains owner and
element then duplicate elements can't be stored in a List. If you want to allow duplicate List
entries, then use the "Join Table" variant above.
There are 2 ways that we can persist this relationship. These are shown below
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address" mapped-by="account">
<order-by>id</order-by>
<join-table name="ACCOUNT_ADDRESSES">
<join-column name="ACCOUNT_ID_OID"/>
<inverse-join-column name="ADDRESS_ID_EID"/>
</join-table>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
<many-to-one name="account">
</many-to-one>
</attributes>
</entity>
</entity-mappings>
@OneToMany(mappedBy="account")
@OrderBy("id")
@JoinTable(name="ACCOUNT_ADDRESSES")
@JoinColumn(name="ACCOUNT_ID_OID")
@InverseJoinColumn(name="ADDRESS_ID_EID")
List<Address> addresses
}
@ManyToOne
Account account;
...
}
The crucial part is the join element on the field element - this signals to JPA to use a join table.
This will create 3 tables in the database, one for Address, one for Account, and a join table, as shown
below.
The join table is used to link the 2 classes via foreign keys to their primary key. This is useful where
you want to retain the independence of one class from the other class.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the class
element
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• To specify the name of the join table, specify the join-table element below the one-to-many
element with the collection.
• To specify the names of the join table columns, use the join-column and inverse-join-column
elements below the join-table element.
• The join table will NOT be given a primary key (so that duplicates can be stored).
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
• If you want to have a surrogate column added to contain the ordering you should specify order-
column (@OrderColumn) instead of order-by. This is available from JPA2
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address" mapped-by="account">
<order-by>city ASC</order-by>
<join-column name="ACCOUNT_ID"/>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
<many-to-one name="account">
</many-to-one>
</attributes>
</entity>
</entity-mappings>
@OneToMany(mappedBy="account")
@OrderBy("city")
@JoinColumn(name="ACCOUNT_ID")
List<Address> addresses
}
@ManyToOne
Account account;
...
}
The crucial part is the mapped-by attribute of the field on the "1" side of the relationship. This tells the JPA
implementation to look for a field called account on the Address class.
This will create 2 tables in the database, one for Address (including an ACCOUNT_ID to link to the
ACCOUNT table), and one for Account. Notice the subtle difference to this set-up to that of the Join
Table relationship earlier.
If you wish to fully define the schema table and column names etc, follow these tips
• To specify the name of the table where a class is stored, specify the table element below the
classelement
• To specify the names of the columns where the fields of a class are stored, specify the column
attribute on the basic element.
• When forming the relation please make sure that you set the relation at BOTH sides since
DataNucleus would have no way of knowing which end is correct if you only set one end.
Limitation : Since each Address object can have at most one owner (due to the "Foreign Key") this
mode of persistence will not allow duplicate values in the Collection. If you want to allow duplicate
Collection entries, then use the "Join Table" variant above.
In JPA1 you cannot have a 1-N List of non-Entity objects. This is available in JPA2. All of the
examples above show a 1-N relationship between 2 persistable classes. If you want the element to
be primitive or Object types then follow this section. For example, when you have a List of Strings.
This will be persisted in the same way as the "Join Table" examples above. A join table is created to
hold the collection elements. Let's take our example. We have an Account that stores a Collection of
addresses. These addresses are simply Strings. We define the annotations like this
@Entity
public class Account
{
...
@ElementCollection
@CollectionTable(name="ACCOUNT_ADDRESSES")
Collection<String> addresses;
}
<entity class="mydomain.Account">
<attributes>
...
<element-collection name="addresses">
<collection-table name="ACCOUNT_ADDRESSES"/>
</element-collection>
</attributes>
</entity>
The ACCOUNT table is as before, but this time we only have the "join table". Use @Column on the
field/method to define the column details of the element in the join table.
142 Maps
.......................................................................................................................................
@Entity
public class Account
{
@OneToMany
@JoinTable
Map<String, Address> addresses;
...
}
@Entity
public class Address {...}
This will create 3 tables in the datastore, one for Account, one for Address and a join table also
containing the key.
You can configure the names of the columns in the join table using the joinColumns attributes of the
various annotations.
Please note that the column ADPT_PK_IDX is added by DataNucleus when the column type of the
key is not valid to be part of a primary key (with the RDBMS being used). If the column type of
your key is acceptable for use as part of a primary key then you will not have this "ADPT_PK_IDX"
column.
@Entity
public class Account
{
@ElementCollection
@CollectionTable
Map<String, String> addresses;
...
}
This results in just 2 tables. The "join" table contains both the key AND the value.
You can configure the names of the columns in the join table using the joinColumns attributes of the
various annotations.
Please note that the column ADPT_PK_IDX is added by DataNucleus when the column type of the
key is not valid to be part of a primary key (with the RDBMS being used). If the column type of
your key is acceptable for use as part of a primary key then you will not have this "ADPT_PK_IDX"
column.
@Entity
public class Account
{
@ElementCollection
@JoinTable
Map<Address, String> addressLookup;
...
}
@Entity
public class Address {...}
This will create 3 tables in the datastore, one for Account, one for Address and a join table also
containing the value.
You can configure the names of the columns in the join table using the joinColumns attributes of the
various annotations.
In this relationship, the Account class has a Map of Address objects, yet the Address knows nothing
about the Account. In this case we don't have a field in the Address to link back to the Account and so
DataNucleus has to use columns in the datastore representation of the Address class. So we define the
MetaData like this
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address">
<map-key name="alias"/>
<join-column name="ACCOUNT_ID_OID"/>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
<basic name="alias">
<column name="KEY" length="20"/>
</basic>
</attributes>
</entity>
</entity-mappings>
Again there will be 2 tables, one for Address, and one for Account. If you wish to specify the names
of the columns used in the schema for the foreign key in the Address table you should use the join-
column element within the field of the map.
In terms of operation within your classes of assigning the objects in the relationship. You have to take
your Account object and add the Address to the Account map field since the Address knows nothing
about the Account. Also be aware that each Address object can have only one owner, since it has a
single foreign key to the Account.
With these classes we want to store a foreign-key in the value table (ADDRESS), and we want to
use the "alias" field in the Address class as the key to the map. If you define the Meta-Data for these
classes as follows
<entity-mappings>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<one-to-many name="addresses" target-entity="com.mydomain.Address" mapped-by="account">
<map-key name="alias"/>
</one-to-many>
</attributes>
</entity>
<entity class="Address">
<table name="ADDRESS"/>
<attributes>
<id name="id">
<column name="ADDRESS_ID"/>
</id>
...
<basic name="alias">
<column name="KEY" length="20"/>
</basic>
<many-to-one name="account">
<join-column name="ACCOUNT_ID_OID"/>
</many-to-one>
</attributes>
</entity>
</entity-mappings>
This will create 2 tables in the datastore. One for Account, and one for Address. The table for Address
will contain the key field as well as an index to the Account record (notated by the mapped-by tag).
so the Account class ("N" side) knows about the User class ("1" side), but not vice-versa. A particular
user could be related to several accounts. If you define the Meta-Data for these classes as follows
<entity-mappings>
<entity class="User">
<table name="USER"/>
<attributes>
<id name="id">
<column name="USER_ID"/>
</id>
...
</attributes>
</entity>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<many-to-one name="user"/>
</attributes>
</entity>
</entity-mappings>
@ManyToOne
User user;
}
This will create 2 tables in the database, one for User (with name USER), and one for Account (with
name ACCOUNT), and a foreign-key in the ACCOUNT table, just like for the case of a OneToOne
relation.
Note that in the case of non-RDBMS datastores there is simply a "column" in the ACCOUNT
"table", storing the "id" of the related object
so the Account class ("N" side) knows about the User class ("1" side), but not vice-versa, and are
using a join table. A particular user could be related to several accounts. If you define the Meta-Data
for these classes as follows
<entity-mappings>
<entity class="User">
<table name="USER"/>
<attributes>
<id name="id">
<column name="USER_ID"/>
</id>
...
</attributes>
</entity>
<entity class="Account">
<table name="ACCOUNT"/>
<attributes>
<id name="id">
<column name="ACCOUNT_ID"/>
</id>
...
<many-to-one name="user">
<join-table name="ACCOUNT_USER"/>
</many-to-one>
</attributes>
</entity>
</entity-mappings>
@ManyToOne
@JoinTable(name="ACCOUNT_USER")
User user;
}
This will create 3 tables in the database, one for User (with name USER), one for Account (with
name ACCOUNT), and a join table (with name ACCOUNT_USER), as shown below.
Note that in the case of non-RDBMS datastores there is no join-table, simply a "column" in the
ACCOUNT "table", storing the "id" of the related object
143.1.3 Bidirectional
This relationship is described in the guide for 1-N relationships. In particular there are 2 ways to
define the relationship for RDBMS : the first uses a Join Table to hold the relationship, whilst the
second uses a Foreign Key in the "N" object to hold the relationship. For non-RDBMS datastores each
side will have a "column" (or equivalent) in the "table" of the N side storing the "id" of the related
(owning) object. Please refer to the 1-N relationships bidirectional relations since they show this exact
relationship.
Here the Product class knows about the Supplier class. In addition the Supplier knows about the
Product class, however with these relationships are really independent.
Please note when adding objects to an M-N relation, you MUST add to the owner side as a
minimum, and optionally also add to the non-owner side. Just adding to the non-owner side will
not add the relation.
The various possible relationships are described below.
<entity-mappings>
<entity class="mydomain.Product">
<table name="PRODUCT"/>
<attributes>
<id name="id">
<column name="PRODUCT_ID"/>
</id>
...
<many-to-many name="suppliers" mapped-by="products">
<join-table name="PRODUCTS_SUPPLIERS">
<join-column name="PRODUCT_ID"/>
<inverse-join-column name="SUPPLIER_ID"/>
</join-table>
</many-to-many>
</attributes>
</entity>
<entity class="mydomain.Supplier">
<table name="SUPPLIER"/>
<attributes>
<id name="id">
<column name="SUPPLIER_ID"/>
</id>
...
<many-to-many name="products"/>
</attributes>
</entity>
</entity-mappings>
@ManyToMany(mappedBy="products")
@JoinTable(name="PRODUCTS_SUPPLIERS",
joinColumns={@JoinColumn(name="PRODUCT_ID")},
inverseJoinColumns={@JoinColumn(name="SUPPLIER_ID")})
Collection<Supplier> suppliers
}
@ManyToMany
Collection<Product> products;
...
}
Note how we have specified the information only once regarding join table name, and join column
names as well as the <join-table>. This is the JPA standard way of specification, and results in a
single join table. The "mapped-by" ties the two fields together.
See also :-
• M-N Worked Example
• M-N with Attributes Worked Example
<entity-mappings>
<entity class="mydomain.Product">
<table name="PRODUCT"/>
<attributes>
<id name="id">
<column name="PRODUCT_ID"/>
</id>
...
<many-to-many name="suppliers" mapped-by="products">
<order-by>name</order-by>
<join-table name="PRODUCTS_SUPPLIERS">
<join-column name="PRODUCT_ID"/>
<inverse-join-column name="SUPPLIER_ID"/>
</join-table>
</many-to-many>
</attributes>
</entity>
<entity class="mydomain.Supplier">
<table name="SUPPLIER"/>
<attributes>
<id name="id">
<column name="SUPPLIER_ID"/>
</id>
...
<many-to-many name="products">
<order-by>name</order-by>
</many-to-many>
</attributes>
</entity>
</entity-mappings>
or using annotations
@ManyToMany
@JoinTable(name="PRODUCTS_SUPPLIERS",
joinColumns={@JoinColumn(name="PRODUCT_ID")},
inverseJoinColumns={@JoinColumn(name="SUPPLIER_ID")})
@OrderBy("id")
List<Supplier> suppliers
}
@ManyToMany
@OrderBy("id")
List<Product> products
}
There will be 3 tables, one for Product, one for Supplier, and the join table. The difference from the
Set example is that we now have <order-by> at both sides of the relation. This has no effect in the
datastore schema but when the Lists are retrieved they are ordered using the specified order-by.
Note that you cannot have a many-to-many relation using indexed lists since both sides would
need its own index.
• To add an object to an M-N relationship you need to set it at both ends of the relation since
the relation is bidirectional and without such information the JPA implementation won't
know which end of the relation is correct.
• If you want to delete an object from one end of a M-N relationship you will have to remove
it first from the other objects relationship. If you don't you will get an error message that
the object to be deleted has links to other objects and so cannot be deleted.
• 1-1 relationships - this is where you have an object A relates to a second object B. The relation
can be unidirectional where A knows about B, but B doesnt know about A. The relation can be
bidirectional where A knows about B and B knows about A.
• 1-N relationships - this is where you have an object A that has a collection of other objects of
type B. The relation can be unidirectional where A knows about the objects B but the Bs dont
know about A. The relation can be bidirectional where A knows about the objects B and the Bs
know about A
• N-1 relationships - this is where you have an object B1 that relates to an object A, and an object
B2 that relates to A also etc. The relation can be unidirectional where the A doesnt know about
the Bs. The relation can be bidirectional where the A has a collection of the Bs. [i.e a 1-N
relationship but from the point of view of the element]
• M-N relationships - this is where you have objects of type A that have a collection of objects
of type B and the objects of type B also have a collection of objects of type A. The relation is
always bidirectional by definition
A a = new A();
B b = new B();
a.setB(b); // "a" knows about "b"
When the relation is bidirectional you have to set both sides of the relation. For example, we have
classes A and B and the class A has a collection of elements of type B, and B has a field of type A. So
we set it like this
A a = new A();
B b1 = new B();
a.addElement(b1); // "a" knows about "b1"
b1.setA(a); // "b1" knows about "a"
So it is really simple, with only 1 real rule. With a bidirectional relation you must set both sides of
the relation
JPA you have to explicitly set this behaviour. For example we have classes A and B and class A has a
field of type B and this field has the cascade property PERSIST set. To persist them we could do
A a = new A();
B b = new B();
a.setB(b); // "a" knows about "b"
em.persist(a);
When performing management of relations there are some checks implemented to spot typical errors in user
operations e.g add an element to a collection and then remove it (why?!). You can disable these checks using
datanucleus.manageRelationshipsChecks, set to false.
146 Cascading
.......................................................................................................................................
@Entity
public class Owner
{
@OneToOne
private DrivingLicense license;
@OneToMany(mappedBy="owner")
private Collection cars;
...
}
@Entity
public class DrivingLicense
{
private String serialNumber;
...
}
@Entity
public class Car
{
private String registrationNumber;
@ManyToOne
private Owner owner;
...
}
So we have an Owner of a collection of vintage Car's, and the Owner has a DrivingLicense. We
want to define lifecycle dependencies to match the relationships that we have between these objects.
So in our example what we are going to do is
• When an object is persisted/updated its related objects are also persisted/updated.
• When an Owner object is deleted, its DrivingLicense is deleted too (since it cant exist without
the person!
• When an Owner object is deleted, the Cars continue to exist (since someone will buy them)
• When a Car object is deleted, the Owner continues to exist (unless he/she dies in the Car, but
that will be handled by a different mechanism in our application).
@Entity
public class Owner
{
@OneToOne(cascade=CascadeType.ALL)
private DrivingLicense license;
...
}
@Entity
public class DrivingLicense
{
private String serialNumber;
...
}
@Entity
public class Car
{
private String registrationNumber;
@ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE})
private Owner owner;
...
}
So we make use of the cascade attribute of the relation annotations. We could express this similarly in
XML
<entity-mappings>
<entity class="mydomain.Owner">
<attributes>
<one-to-many name="cars">
<cascade>
<cascade-persist/>
<cascade-merge/>
</cascade>
</one-to-many>
<one-to-one name="license">
<cascade>
<cascade-all/>
</cascade>
</one-to-one>
...
</attributes>
</entity>
<entity class="mydomain.DrivingLicense">
...
</entity>
<entity class="mydomain.Car">
<attributes>
<many-to-one name="owner">
<cascade>
<cascade-persist/>
<cascade-merge/>
</cascade>
</many-to-one>
...
</attributes>
</entity>
</entity-mappings>
146.1.1 Orphans
When an element is removed from a collection, or when a 1-1 relation is nulled, sometimes it is
desirable to delete the other object. JPA2 defines a facility of removing "orphans" by specifying
metadata for a 1-1 or 1-N relation. Let's take an example. In the above relation between Owner and
DrivingLicense if we set the owners license field to null, this should mean that the license is deleted.
So we could change it to be
@Entity
public class Owner
{
@OneToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval=true)
private DrivingLicense license;
...
}
@Entity
public class DrivingLicense
{
private String serialNumber;
...
}
So from now on, if we delete the Owner we delete the DrivingLicense, and if we set the license field
of DrivingLicense to null then we also delete the DrivingLicense.
• XML : the traditional mechanism, with XML files containing information for each class to be
persisted.
• Annotations : using JDK1.5+ annotations in the classes to be persisted
We recommend that you use either XML or annotations for the basic persistence information, but always use
XML for ORM information. This is because it is liable to change at deployment time and hence is accessible
when in XML form whereas in annotations you add an extra compile cycle (and also you may need to deploy to
some other datastore at some point, hence needing a different deployment).
148 XML
.......................................................................................................................................
If using any of the DataNucleus extensions, then the XSD is defined here, in which case you would
define your header as :-
• entity-mappings
• description
• persistence-unit-metadata
• xml-mapping-metadata-complete
• package
• schema
• catalog
• access
• sequence-generator
• table-generator
• named-query
• query
• named-native-query
• query
• sql-result-set-mapping
• entity-result
• field-result
• column-result
• mapped-superclass
• description
• id-class
• exclude-default-listeners
• exclude-superclass-listeners
• entity-listeners
• entity-listener
• pre-persist
• post-persist
• pre-remove
• post-remove
• pre-update
• post-update
• post-load
• pre-persist
• post-persist
• pre-remove
• post-remove
• pre-update
• post-update
• post-load
• attributes
• description
• table
• unique-constraint
• column-name
• index
• secondary-table
• primary-key-join-column
• primary-key-foreign-key
• unique-constraint
• column-name
• index
• primary-key-join-column
• primary-key-foreign-key
• id-class
• inheritance
• discriminator-value
• discriminator-column
• sequence-generator
• table-generator
• index
• named-query
• query
• named-native-query
• query
• sql-result-set-mapping
• entity-result
• field-result
• column-result
• named-entity-graph
• named-attribute-node
• subgraph
• named-attribute-node
• subclass-subgraph
• named-attribute-node
• exclude-default-listeners
• exclude-superclass-listeners
• entity-listeners
• entity-listener
• pre-persist
• post-persist
• pre-remove
• post-remove
• pre-update
• post-update
• post-load
• pre-persist
• post-persist
• pre-remove
• post-remove
• pre-update
• post-update
• post-load
• attribute-override
• column
• association-override
• join-column
• attributes
• id
• column
• generated-value
• sequence-generator
• table-generator
• embedded-id
• basic
• column
• lob
• temporal
• enumerated
• convert
• version
• column
• many-to-one
• join-column
• join-table
• join-column
• inverse-join-column
• unique-constraint
• column-name
• cascade
• cascade-all
• cascade-persist
• cascade-merge
• cascade-remove
• cascade-refresh
• element-collection
• collection-table
• join-column
• index
• foreign-key
• order-by
• order-column
• map-key
• map-key-temporal
• map-key-enumerated
• join-table
• join-column
• foreign-key
• inverse-join-column
• inverse-foreign-key
• unique-constraint
• column-name
• join-column
• one-to-many
• order-by
• order-column
• map-key
• map-key-temporal
• map-key-enumerated
• join-table
• join-column
• inverse-join-column
• unique-constraint
• column-name
• join-column
• cascade
• cascade-all
• cascade-persist
• cascade-merge
• cascade-remove
• cascade-refresh
• one-to-one
• join-column
• foreign-key
• join-table
• join-column
• inverse-join-column
• unique-constraint
• column-name
• cascade
• cascade-all
• cascade-persist
• cascade-merge
• cascade-remove
• cascade-refresh
• many-to-many
• order-by
• order-column
• map-key
• map-key-temporal
• map-key-enumerated
• join-table
• join-column
• inverse-join-column
• unique-constraint
• column-name
• cascade
• cascade-all
• cascade-persist
• cascade-merge
• cascade-remove
• cascade-refresh
• embedded
• attribute-override
• transient
• embeddable
• embeddable-attributes
• basic
• transient
149 Annotations
.......................................................................................................................................
149.1.1 @Entity
This annotation is used when you want to mark a class as persistent. Specified on the class.
@Entity
public class MyClass
{
...
}
149.1.2 @MappedSuperclass
This annotation is used when you want to mark a class as persistent but without a table of its own and
being the superclass of the class that has a table, meaning that all of its fields are persisted into the
table of its subclass. Specified on the class.
@MappedSuperclass
public class MyClass
{
...
}
149.1.3 @PersistenceAware
This annotation is used when you want to mark a class as knowing about persistence but not persistent
itself. That is, it manipulates the fields of a persistent class directly rather than using accessors. This is
a DataNucleus extension. Specified on the class.
@PersistenceAware
public class MyClass
{
...
}
149.1.4 @Embeddable
This annotation is used when you want to mark a class as persistent and only storable embedded in
another object. Specified on the class.
@Embeddable
public class MyClass
{
...
}
149.1.5 @Cacheable
This annotation is used when you want to mark a class so that instance of that class can be cached.
Specified on the class.
@Cacheable
public class MyClass
{
...
}
149.1.6 @Inheritance
This annotation is used to define the inheritance persistence for this class. Specified on the class.
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class MyClass
{
...
}
149.1.7 @Table
This annotation is used to define the table where objects of a class will be stored. Specified on the
class.
@Entity
@Table(name="MYTABLE", schema="PUBLIC")
public class MyClass
{
...
}
149.1.8 @SecondaryTable
This annotation is used to define a secondary table where some fields of this class are stored in
another table. Specified on the class.
@Entity
@Table(name="MYTABLE", schema="PUBLIC")
@SecondaryTable(name="MYOTHERTABLE", schema="PUBLIC", columns={@PrimaryKeyJoinColumn(name="MYCLASS_ID")})
public class MyClass
{
...
}
149.1.9 @IdClass
This annotation is used to define a primary-key class for the identity of this class. Specified on the
class.
@Entity
@IdClass(org.datanucleus.samples.MyIdentity.class)
public class MyClass
{
...
}
149.1.10 @DatastoreIdentity
This DataNucleus-extension annotation is used to define that the class uses datastore-identity.
Specified on the class.
@Entity
@DatastoreIdentity(column="MY_ID")
public class MyClass
{
...
}
149.1.11 @EntityListeners
This annotation is used to define a class or classes that are listeners for events from instances of this
class. Specified on the class.
@Entity
@EntityListeners(org.datanucleus.MyListener.class)
public class MyClass
{
...
}
149.1.12 @NamedQueries
This annotation is used to define a series of named (JPQL) queries that can be used in this persistence
unit. Specified on the class.
@Entity
@NamedQueries({
@NamedQuery(name="AllPeople",
query="SELECT p FROM Person p"),
@NamedQuery(name="PeopleCalledJones",
query="SELECT p FROM Person p WHERE p.surname = 'Jones'")})
public class Person
{
...
}
Note that with DataNucleus you can also specify @NamedQueries on non-persistable classes
See the documentation for Named Queries
149.1.13 @NamedQuery
This annotation is used to define a named (JPQL) query that can be used in this persistence unit.
Specified on the class.
@Entity
@NamedQuery(name="AllPeople", query="SELECT p FROM Person p")
public class Person
{
...
}
Note that with DataNucleus you can also specify @NamedQuery on non-persistable classes
See the documentation for Named Queries
149.1.14 @NamedNativeQueries
This annotation is used to define a series of named native (SQL) queries that can be used in this
persistence unit. Specified on the class.
@Entity
@NamedNativeQueries({
@NamedNativeQuery(name="AllPeople",
query="SELECT * FROM PERSON WHERE SURNAME = 'Smith'"),
@NamedNativeQuery(name="PeopleCalledJones",
query="SELECT * FROM PERSON WHERE SURNAME = 'Jones')})
public class Person
{
...
}
Note that with DataNucleus you can also specify @NamedNativeQueries on non-persistable
classes
See the documentation for Named Native Queries
149.1.15 @NamedNativeQuery
This annotation is used to define a named (SQL) query that can be used in this persistence unit.
Specified on the class.
@Entity
@NamedNativeQuery(name="PeopleCalledSmith", query="SELECT * FROM PERSON WHERE SURNAME = 'Smith'")
public class Person
{
...
}
Note that with DataNucleus you can also specify @NamedNativeQuery on non-persistable
classes
See the documentation for Named Native Queries
149.1.16 @NamedStoredProcedureQueries
This annotation is used to define a series of named native stored procedure queries that can be used in
this persistence unit. Specified on the class.
@Entity
@NamedStoredProcedureQueries({
@NamedStoredProcedureQuery(name="MyProc", procedureName="MY_PROC_SP1",
parameters={@StoredProcedureParameter(name="PARAM1", mode=ParameterMode.IN, type=String.class)}),
@NamedStoredProcedureQuery(name="MyProc2", procedureName="MY_PROC_SP2",
parameters={@StoredProcedureParameter(name="PARAM1", mode=ParameterMode.IN, type=Long.class)})})
public class Person
{
...
}
Note that with DataNucleus you can also specify @NamedStoredProcedureQueries on non-
persistable classes
See the documentation for Named Stored procedures
149.1.17 @NamedStoredProcedureQuery
This annotation is used to define a named stored procedure query that can be used in this persistence
unit. Specified on the class.
@Entity
@NamedStoredProcedureQuery(name="MyProc", procedureName="MY_PROC_SP1",
parameters={@StoredProcedureParameter(name="PARAM1", mode=ParameterMode.IN, type=String.class)})
public class Person
{
...
}
Note that with DataNucleus you can also specify @NamedStoredProcedureQuery on non-
persistable classes
See the documentation for Named StoredProcedures
149.1.18 @SqlResultSetMappings
This annotation is used to define a series of result mappings for SQL queries that can be used in this
persistence unit. Specified on the class.
@Entity
@SqlResultSetMappings({
@SqlResultSetMapping(name="PEOPLE_PLUS_AGE",
entities={@EntityResult(entityClass=Person.class)}, columns={@ColumnResult(name="AGE")}),
@SqlResultSetMapping(name="FIRST_LAST_NAMES",
columns={@ColumnResult(name="FIRSTNAME"), @ColumnResult(name="LASTNAME")})
})
public class Person
{
...
}
149.1.19 @SqlResultSetMapping
This annotation is used to define a mapping for the results of an SQL query and can be used in this
persistence unit. Specified on the class.
@Entity
@SqlResultSetMapping(name="PEOPLE_PLUS_AGE",
entities={@EntityResult(entityClass=Person.class)}, columns={@ColumnResult(name="AGE")})
public class Person
{
...
}
149.1.20 @NamedEntityGraphs
This annotation is used to define a series of named EntityGraphs that can be used in this persistence
unit. Specified on the class.
@Entity
@NamedEntityGraph({
@NamedEntityGraph(name="PERSON_FULL",
attributeNodes={@NamedAttributeNode(name="friends"), @NamedAttributeNode(name="parents")}),
@NamedEntityGraph(name="PERSON_BASIC",
attributeNodes={@NamedAttributeNode(name="parents")})
})
public class Person
{
...
}
149.1.21 @NamedEntityGraph
This annotation is used to define a named EntityGraph and can be used in this persistence unit.
Specified on the class.
@Entity
@NamedEntityGraph(name="PERSON_FULL",
attributeNodes={@NamedAttributeNode(name="friends"), @NamedAttributeNode(name="parents")})
public class Person
{
...
}
149.1.22 @PrePersist
This annotation is used to define a method that is a callback for pre-persist events. Specified on the
method. It has no attributes.
@Entity
public class MyClass
{
...
@PrePersist
void registerObject()
{
...
}
}
149.1.23 @PostPersist
This annotation is used to define a method that is a callback for post-persist events. Specified on the
method. It has no attributes.
@Entity
public class MyClass
{
...
@PostPersist
void doSomething()
{
...
}
}
149.1.24 @PreRemove
This annotation is used to define a method that is a callback for pre-remove events. Specified on the
method. It has no attributes.
@Entity
public class MyClass
{
...
@PreRemove
void registerObject()
{
...
}
}
149.1.25 @PostRemove
This annotation is used to define a method that is a callback for post-remove events. Specified on the
method. It has no attributes.
@Entity
public class MyClass
{
...
@PostRemove
void doSomething()
{
...
}
}
149.1.26 @PreUpdate
This annotation is used to define a method that is a callback for pre-update events. Specified on the
method. It has no attributes.
@Entity
public class MyClass
{
...
@PreUpdate
void registerObject()
{
...
}
}
149.1.27 @PostUpdate
This annotation is used to define a method that is a callback for post-update events. Specified on the
method. It has no attributes.
@Entity
public class MyClass
{
...
@PostUpdate
void doSomething()
{
...
}
}
149.1.28 @PostLoad
This annotation is used to define a method that is a callback for post-load events. Specified on the
method. It has no attributes.
@Entity
public class MyClass
{
...
@PostLoad
void registerObject()
{
...
}
}
149.1.29 @SequenceGenerator
This annotation is used to define a generator using sequences in the datastore. It is scoped to the
persistence unit. Specified on the class/field/method.
@Entity
@SequenceGenerator(name="MySeq", sequenceName="SEQ_2")
public class MyClass
{
...
}
149.1.30 @TableGenerator
This annotation is used to define a generator using a table in the datastore for storing the values. It is
scoped to the persistence unit. Specified on the class/field/method.
@Entity
@TableGenerator(name="MySeq", table="MYAPP_IDENTITIES", pkColumnValue="MyClass")
public class MyClass
{
...
}
149.1.31 @DiscriminatorColumn
This annotation is used to define the discriminator column for a class. Specified on the class.
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="OBJECT_TYPE", discriminatorType=DiscriminatorType.STRING)
public class MyClass
{
...
}
149.1.32 @DiscriminatorValue
This annotation is used to define the value to be stored in the discriminator column for a class (when
used). Specified on the class.
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="OBJECT_TYPE", discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("MyClass")
public class MyClass
{
...
}
149.1.33 @PrimaryKeyJoinColumns
This annotation is used to define the names of the primary key columns when this class has a
superclass. Specified on the class.
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@PrimaryKeyJoinColumns({@PrimaryKeyJoinColumn(name="PK_FIELD_1", referredColumnName="BASE_1_ID"),
@PrimaryKeyJoinColumn(name="PK_FIELD_2", referredColumnName="BASE_2_ID")})
public class MyClass
{
...
}
149.1.34 @PrimaryKeyJoinColumn
This annotation is used to define the name of the primary key column when this class has a superclass.
Specified on the class.
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@PrimaryKeyJoinColumn(name="PK_FIELD_1")
public class MyClass
{
...
}
149.1.35 @AttributeOverride
This annotation is used to define a field of a superclass that has its column overridden. Specified on
the class.
@Entity
@AttributeOverride(name="attr", column=@Column(name="NEW_NAME"))
public class MyClass extends MySuperClass
{
...
}
149.1.36 @AttributeOverrides
This annotation is used to define fields of a superclass that have their columns overridden. Specified
on the class.
@Entity
@AttributeOverrides({@AttributeOverride(name="attr1", column=@Column(name="NEW_NAME_1")),
@AttributeOverride(name="attr2", column=@Column(name="NEW_NAME_2"))})
public class MyClass extends MySuperClass
{
...
}
149.1.37 @AssociationOverride
This annotation is used to define a 1-1/N-1 field of a superclass that has its column overridden.
Specified on the class.
@Entity
@AssociationOverride(name="friend", joinColumn=@JoinColumn(name="FRIEND_ID"))
public class Employee extends Person
{
...
}
149.1.38 @AssociationOverrides
This annotation is used to define 1-1/N-1 fields of a superclass that have their columns overridden.
Specified on the class.
@Entity
@AssociationOverrides({@AssociationOverride(name="friend", joinColumn=@JoinColumn(name="FRIEND_ID")),
@AssociationOverride(name="teacher", joinColumn=@JoinColumn(name="TEACHER_ID"))})
public class Employee extends Person
{
...
}
149.1.39 @Id
This annotation is used to define a field to use for the identity of the class. Specified on the field/
method.
@Entity
public class MyClass
{
@Id
long id;
...
}
149.1.40 @Embedded
This annotation is used to define a field as being embedded. Specified on the field/method.
@Entity
public class MyClass
{
@Embedded
Object myField;
...
}
149.1.41 @EmbeddedId
This annotation is used to define a field to use for the identity of the class when embedded. Specified
on the field/method.
@Entity
public class MyClass
{
@EmbeddedId
MyPrimaryKey pk;
...
}
149.1.42 @Version
This annotation is used to define a field as holding the version for the class. Specified on the field/
method.
@Entity
public class MyClass
{
@Id
long id;
@Version
int ver;
...
}
149.1.43 @Basic
This annotation is used to define a field of the class as persistent. Specified on the field/method.
@Entity
public class Person
{
@Id
long id;
@Basic(optional=false)
String forename;
...
}
149.1.44 @Transient
This annotation is used to define a field of the class as not persistent. Specified on the field/method.
@Entity
public class Person
{
@Id
long id;
@Transient
String personalInformation;
...
}
149.1.45 @JoinTable
This annotation is used to define that a collection/map is stored using a join table. Specified on the
field/method.
@Entity
public class Person
{
@OneToMany
@JoinTable(name="PEOPLES_FRIENDS")
Collection friends;
...
}
149.1.46 @CollectionTable
This annotation is used to define that a collection/map of non-entities is stored using a join table.
Specified on the field/method.
@Entity
public class Person
{
@ElementCollection
@CollectionTable(name="PEOPLES_FRIENDS")
Collection<String> values;
...
}
149.1.47 @Lob
This annotation is used to define that a field will be stored using a large object in the datastore.
Specified on the field/method.
@Entity
public class Person
{
@Lob
byte[] photo;
...
}
149.1.48 @Temporal
This annotation is used to define that a field is stored as a temporal type. It specifies the JDBC type
to use for storage of this type, so whether it stores the date, the time, or both. Specified on the field/
method.
@Entity
public class Person
{
@Temporal(TemporalType.TIMESTAMP)
java.util.Date dateOfBirth;
...
}
149.1.49 @Enumerated
This annotation is used to define that a field is stored enumerated (not that it wasnt obvious from the
type!). Specified on the field/method.
@Entity
public class Person
{
@Enumerated
Gender gender;
...
}
149.1.50 @OneToOne
This annotation is used to define that a field represents a 1-1 relation. Specified on the field/method.
@Entity
public class Person
{
@OneToOne
Person bestFriend;
...
}
149.1.51 @OneToMany
This annotation is used to define that a field represents a 1-N relation. Specified on the field/method.
@Entity
public class Person
{
@OneToMany
Collection<Person> friends;
...
}
149.1.52 @ManyToMany
This annotation is used to define that a field represents a M-N relation. Specified on the field/method.
@Entity
public class Customer
{
@ManyToMany(mappedBy="customers")
Collection<Supplier> suppliers;
...
}
@Entity
public class Supplier
{
@ManyToMany
Collection<Customer> customers;
...
}
149.1.53 @ManyToOne
This annotation is used to define that a field represents a N-1 relation. Specified on the field/method.
@Entity
public class House
{
@OneToMany(mappedBy="house")
Collection<Window> windows;
...
}
@Entity
public class Window
{
@ManyToOne
House house;
...
}
149.1.54 @ElementCollection
This annotation is used to define that a field represents a 1-N relation to non-entity objects. Specified
on the field/method.
@Entity
public class Person
{
@ElementCollection
Collection<String> values;
...
}
@Entity
public class MyClass
{
@Index(name="ENABLED_IDX")
boolean enabled;
...
}
149.1.56 @JdbcType
This DataNucleus-extension annotation is used to define the jdbc-type to use for this field/property.
Specified on the field/property.
@Entity
public class MyClass
{
@JdbcType("CHAR")
boolean enabled;
...
}
149.1.57 @ColumnPosition
This DataNucleus-extension annotation is used to define the column position to use for this field/
property. Specified on the field/property.
@Entity
public class MyClass
{
@ColumnPosition(0)
boolean enabled;
...
}
149.1.58 @GeneratedValue
This annotation is used to define the generation of a value for a (PK) field. Specified on the field/
method.
@Entity
public class Person
{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
long id;
...
}
149.1.59 @MapKey
This annotation is used to define the field in the value class that represents the key in a Map. Specified
on the field/method.
@Entity
public class Person
{
@OneToMany
@MapKey(name="nickname")
Map<String, Person> friends;
...
}
149.1.60 @MapKeyTemporal
This annotation is used to define the datastore type used for the key of a map when it is a temporal
type. Specified on the field/method.
@Entity
public class Person
{
@ElementCollection
@MapKeyTemporal(TemporalType.DATE)
Map<Date, String> dateMap;
...
}
149.1.61 @MapKeyEnumerated
This annotation is used to define the datastore type used for the key of a map when it is an enum.
Specified on the field/method.
@Entity
public class Person
{
@ElementCollection
@MapKeyEnumerated(EnumType.STRING)
Map<MyEnum, String> dateMap;
...
}
149.1.62 @MapKeyColumn
This annotation is used to define the column details for a key of a Map when stored in a join table.
Specified on the field/method.
@Entity
public class Person
{
@OneToMany
@MapKeyColumn(name="FRIEND_NAME")
Map<String, Person> friends;
...
}
149.1.63 @OrderBy
This annotation is used to define a field in the element class that is used for ordering the elements of
the List when it is retrieved. Specified on the field/method.
@Entity
public class Person
{
@OneToMany
@OrderBy(value="nickname")
List<Person> friends;
...
}
149.1.64 @OrderColumn
This annotation is used to definethat the JPA implementation will handle the ordering of the List
elements using a surrogate column. Specified on the field/method.
@Entity
public class Person
{
@OneToMany
@OrderColumn
List<Person> friends;
...
}
149.1.65 @Convert
This annotation is used to define a converter for the field/property. Specified on the field/method.
@Entity
public class Person
{
@Basic
@Convert(converter=MyURLConverter.class)
URL website;
...
}
149.1.66 @Converter
This annotation is used to mark a class as being an attribute converter. Specified on the field/method.
@Converter
public class MyConverter
{
...
}
149.1.67 @Column
This annotation is used to define the column where a field is stored. Specified on the field/method.
@Entity
public class Person
{
@Basic
@Column(name="SURNAME", length=100, nullable=false)
String surname;
...
}
149.1.68 @JoinColumn
This annotation is used to define the FK column for joining to another table. This is part of a 1-1, 1-N,
or N-1 relation. Specified on the field/method.
@Entity
public class Person
{
@OneToOne
@JoinColumn(name="PET_ID", nullable=true)
Animal pet;
...
}
149.1.69 @JoinColumns
This annotation is used to define the FK columns for joining to another table. This is part of a 1-1, 1-
N, or N-1 relation. Specified on the field/method.
@Entity
public class Person
{
@OneToOne
@JoinColumns({@JoinColumn(name="PET1_ID"), @JoinColumn(name="PET2_ID")})
Animal pet; // composite PK
...
}
149.1.70 @UniqueConstraint
This annotation is used to define a unique constraint to apply to a table. It is specified as part of
@Table, @JoinTable or @SecondaryTable.
@Entity
@Table(name="PERSON", uniqueConstraints={@UniqueConstraint(columnNames={"firstName","lastName"})})
public class Person
{
@Basic
String firstName;
@Basic
String lastName;
...
}
149.1.71 @Index
This annotation is used to define the details for an Index. It is specified as part of @Table,
@JoinTable, @CollectionTable or @SecondaryTable.
149.1.72 @ForeignKey
This annotation is used to define the details for a ForeignKey. It is specified as part of @JoinColumn,
@JoinTable, @CollectionTable or @SecondaryTable.
149.1.73 @Extensions
DataNucleus Extension Annotation used to define a set of extensions specific to DataNucleus.
Specified on the class or field.
@Entity
@Extensions({@Extension(key="firstExtension", value="myValue"),
@Extension(key="secondExtension", value="myValue")})
public class Person
{
...
}
149.1.74 @Extension
DataNucleus Extension Annotation used to define an extension specific to DataNucleus. Specified on
the class or field.
@Entity
@Extension(key="RunFast", value="true")
public class Person
{
...
}
In our case we want to map this class to a table called ESTABLISHMENT, and has columns NAME,
DIRECTION, PHONE and NUMBER_OF_ROOMS (amongst other things). So we define our Meta-
Data like this
<entity class="Hotel">
<table name="ESTABLISHMENT"/>
<attributes>
<basic name="name">
<column name="NAME"/>
</basic>
<basic name="address">
<column name="DIRECTION"/>
</basic>
<basic name="telephoneNumber">
<column name="PHONE"/>
</basic>
<basic name="numberOfRooms">
<column name="NUMBER_OF_ROOMS"/>
</basic>
</attributes>
</entity>
So we have defined the table and the column names. It should be mentioned that if you don't specify
the table and column names then DataNucleus will generate names for the datastore identifiers
consistent with the JPA specification. The table name will be based on the class name, and the column
names will be based on the field names and the role of the field (if part of a relationship).
See also :-
In this class we can model payments from a customer of an amount. Where the customer pays by bank
transfer we can save the reference number. Since the bank transfer reference is optional we want that
column to be nullable. So let's specify the MetaData for the class.
<entity class="Payment">
<attributes>
<one-to-one name="customer">
<primary-key-join-column name="CUSTOMER_ID"/>
</one-to-one>
<basic name="bankTransferReference">
<column name="TRANSFER_REF" nullable="true"/>
</basic>
<basic name="currency">
<column name="CURRENCY" default-value="GBP"/>
</basic>
<basic name="amount">
<column name="AMOUNT"/>
</basic>
</attributes>
</entity>
So we make use of the nullable attribute. The table, when created by DataNucleus, will then provide
the nullability that we require. Unfortunately with JPA there is no way to specify a default value for a
field when it hasnt been set (unlike JDO where you can do that).
See also :-
...
}
So we defined the JDBC type that this field will use (rather than the default of VARCHAR).
JPA does allow permit control over the length/precision/scale of columns. So we define this as
follows
<entity name="Payment">
<attributes>
<one-to-one name="customer">
<primary-key-join-column name="CUSTOMER_ID"/>
</one-to-one>
<basic name="bankTransferReference">
<column name="TRANSFER_REF" nullable="true" length="255"/>
</basic>
<basic name="currency">
<column name="CURRENCY" default-value="GBP" length="3"/>
</basic>
<basic name="amount">
<column name="AMOUNT" precision="10" scale="2"/>
</basic>
</attributes>
</entity>
150.1.4 columnposition
With some datastores it is desirable to be able to specify the relative position of a column in the
table schema. The default (for DataNucleus) is just to put them in ascending alphabetical order.
DataNucleus allows an extension to JPA providing definition of this using the position of a column.
See fields/properties column positioning docs for details.
151 Multitenancy
.......................................................................................................................................
<class name="MyClass">
<extension vendor-name="datanucleus" key="multitenancy-column-name" value="TENANT"/>
<extension vendor-name="datanucleus" key="multitenancy-column-length" value="24"/>
...
</class>
In all subsequent use of DataNucleus, any "insert" to the primary "table"(s) will also include the
TENANT column value. Additionally any query will apply a WHERE clause restricting to a
particular value of TENANT column.
If you want to disable multitenancy on a class, just specify the following metadata
<class name="MyClass">
<extension vendor-name="datanucleus" key="multitenancy-disable" value="true"/>
...
</class>
class MyClass
{
String myField1;
Collection<MyElement> elements1; // Using join table
Collection<MyElement> elements2; // Using foreign-key
}
class MyElement
{
String myElementField;
MyClass myClass2;
}
The NamingFactory "jpa" aims at providing a naming policy consistent with the "JPA" specification.
Using the same example above, the rules in this NamingFactory mean that, assuming that the user
doesn't specify any <column> elements :-
• MyClass will be persisted into a table named MYCLASS
• When using datastore identity MYCLASS will have a column called MYCLASS_ID
• MyClass.myField1 will be persisted into a column called MYFIELD1
Applicable to RDBMS
The standard JPA persistence strategy is to persist an object of a class into its own table. In some
situations you may wish to map the class to a primary table as well as one or more secondary tables.
For example when you have a Java class that could have been split up into 2 separate classes yet, for
whatever reason, has been written as a single class, however you have a legacy datastore and you need
to map objects of this class into 2 tables. JPA allows persistence of fields of a class into secondary
tables.
The process for managing this situation is best demonstrated with an example. Let's suppose we have
a class that represents a Printer. The Printer class contains within it various attributes of the toner
cartridge. So we have
package com.mydomain.samples.secondarytable;
String tonerModel;
int tonerLifetime;
/**
* Constructor.
* @param make Make of printer (e.g Hewlett-Packard)
* @param model Model of Printer (e.g LaserJet 1200L)
* @param tonerModel Model of toner cartridge
* @param tonerLifetime lifetime of toner (number of prints)
*/
public Printer(String make, String model, String tonerModel, int tonerLifetime)
{
this.make = make;
this.model = model;
this.tonerModel = tonerModel;
this.tonerLifetime = tonerLifetime;
}
Now we have a database schema that has 2 tables (PRINTER and PRINTER_TONER) in which to
store objects of this class. So we need to tell DataNucleus to perform this mapping. So we define the
MetaData for the Printer class like this
<entity class="Printer">
<table name="PRINTER"/>
<secondary-table name="PRINTER_TONER">
<primary-key-join-column name="PRINTER_REFID"/>
</secondary-table>
<attributes>
<id name="id">
<column name="PRINTER_ID"/>
</id>
<basic name="make">
<column name="MAKE" length="40"/>
</basic>
<basic name="model">
<column name="MODEL" length="100"/>
</basic>
<basic name="tonerModel">
<column name="MODEL" table="PRINTER_TONER"/>
</basic>
<basic name="tonerLifetime">
<column name="LIFETIME" table="PRINTER_TONER"/>
</basic>
</attributes>
</entity>
So here we have defined that objects of the Printer class will be stored in the primary table
PRINTER. In addition we have defined that some fields are stored in the table PRINTER_TONER.
• We declare the "secondary-table"(s) that we will be using at the start of the definition.
• We define tonerModel and tonerLifetime to use columns in the table PRINTER_TONER. This
uses the "table" attribute of <column>
• Whilst defining the secondary table(s) we will be using, we also define the join column to be
called "PRINTER_REFID".
This results in the following database tables :-
So we now have our primary and secondary database tables. The primary key of the
PRINTER_TONER table serves as a foreign key to the primary class. Whenever we persist a Printer
object a row will be inserted into both of these tables.
See also :-
154 Constraints
.......................................................................................................................................
• Indexes - these are used to mark fields that are referenced often as indexes so that when they are
used the performance is optimised.
• Unique constraints - these are placed on fields that should have a unique value. That is, only one
object will have a particular value.
• Foreign-Keys - these are used to interrelate objects, and allow the datastore to keep the integrity
of the data in the datastore.
• Primary-Keys - allow the PK to be set, and also to have a name.
154.1.1 Indexes
Many datastores provide the ability to have indexes defined to give performance benefits. With
RDBMS the indexes are specified on the table and the indexes to the rows are stored separately. In
the same way an ODBMS typically allows indexes to be specified on the fields of the class, and these
are managed by the datastore. JPA 2.1 allows you to define the indexes on a table-by-table basis by
metadata as in the following example (note that you cannot specify indexes on a field basis like in
JDO)
import javax.persistence.Index;
@Entity
@Table(indexes={@Index(name="SOME_VAL_IDX", columnList="SOME_VALUE")})
public class MyClass
{
@Column(name="SOME_VALUE")
long someValue;
...
}
The JPA @Index annotation is only applicable at a class level. DataNucleus provides its own @Index
annotation that you can specify on a field/method to signify that the column(s) for this field/method
will be indexed. Like this
@Entity
public class MyClass
{
@org.datanucleus.api.jpa.annotations.Index(name="VAL_IDX")
long someValue;
...
}
Some datastores provide the ability to have unique constraints defined on tables to give extra control
over data integrity. JPA1 provides a mechanism for defining such unique constraints. Let's take an
example class, and show how to specify this
and here we want to impose uniqueness on the "nickname" field, so there is only one Person known as
"DataNucleus Guru" for example !
<entity class="Person">
<table name="PEOPLE"/>
<attributes>
...
<basic name="nickname">
<column name="SURNAME" unique="true"/>
</basic>
...
</attributes>
</entity>
The second use of unique constraints is where we want to impose uniqueness across composite
columns. So we reuse the class above, and this time we want to impose a constraint that there is only
one Person with a particular "forename+surname".
<entity class="Person">
<table name="PEOPLE">
<unique-constraint>
<column-name>FORENAME</column-name>
<column-name>SURNAME</column-name>
</unique-constraint>
</table>
<attributes>
...
<basic name="forename">
<column name="FORENAME"/>
</basic>
<basic name="surname">
<column name="SURNAME"/>
</basic>
...
</attributes>
</entity>
In the same way we can also impose unique constraints on <join-table> and <secondary-table>
See also :-
Applicable to RDBMS
When objects have relationships with one object containing, for example, a Collection of another
object, it is common to store a foreign key in the datastore representation to link the two associated
tables. Moreover, it is common to define behaviour about what happens to the dependent object when
the owning object is deleted. Should the deletion of the owner cause the deletion of the dependent
object maybe ? JPA 2.1 adds support for defining the foreign key for relation fields as per the
following example
@OneToOne
@JoinColumn(name="OTHER_ID", foreignKey=@ForeignKey(name="OTHER_FK",
foreignKeyDefinition="FOREIGN KEY (OTHER_ID) REFERENCES MY_OTHER_TBL (MY_OTHER_ID) ]"))
MyOtherClass other;
Note that when you don't specify any foreign key the JPA provider is free to add the foreign keys that
it considers are necessary.
Applicable to RDBMS
In RDBMS datastores, it is accepted as good practice to have a primary key on all tables. You specify
in other parts of the MetaData which fields are part of the primary key (if using application identity).
Unfortunately JPA1 doesnt allow specification of the name of the primary key constraint, nor of
whether join tables are given a primary key constraint at all.
155 Enhancer
.......................................................................................................................................
• Post-compilation
• Runtime Enhancement
• Programmatically via an API
155.1.1 Maven
Maven operates from a series of plugins. There is a DataNucleus plugin for Maven that allows
enhancement of classes. Go to the Download section of the website and download this. Once you
have the Maven plugin, you then need to set any properties for the plugin in your pom.xml file. Some
properties that you may need to change are below
You will need to add (org.datanucleus) datanucleus-api-jpa into the CLASSPATH (of the plugin, or
your project) for the enhancer to operate. Similarly persistence-api (but then you almost certainly will
have that in your project CLASSPATH anyway).
You then run the Maven DataNucleus plugin, as follows
mvn datanucleus:enhance
This will enhance all classes for the specified persistence-unit. If you want to check the current status
of enhancement you can also type
mvn datanucleus:enhance-check
<build>
...
<plugins>
<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-maven-plugin</artifactId>
<version>4.0.0-release</version>
<configuration>
<api>JPA</api>
<persistenceUnitName>MyUnit</persistenceUnitName>
<log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
...
</build>
So you then get auto-enhancement after each compile. Please refer to the Maven JPA guide for more
details.
155.1.2 Ant
Ant provides a powerful framework for performing tasks. DataNucleus provides an Ant task to
enhance classes. DataNucleus provides an Enhancer in datanucleus-core.jar. You need to make sure
that the datanucleus-core.jar, datanucleus-api-jpa.jar, log4j.jar (optional), and persistence-api.jar are
in your CLASSPATH. In the DataNucleus Enhancer Ant task, the following parameters are available
The enhancer task extends the Apache Ant Java task, thus all parameters available to the Java task are
also available to the enhancer task.
So you could define something like the following, setting up the parameter enhancer.classpath, and
log4j.config.file to suit your situation.
<datanucleusenhancer
persistenceUnit="MyUnit" failonerror="true" verbose="true">
<jvmarg line="-Dlog4j.configuration=${log4j.config.file}"/>
<classpath>
<path refid="enhancer.classpath"/>
</classpath>
</datanucleusenhancer>
</target>
155.1.3 Manually
DataNucleus provides an Enhancer in datanucleus-core.jar. If you are building your application
manually and want to enhance your classes you follow the instructions in this section. You invoke the
enhancer as follows
where "mapping-files" and "class-files" are provided when not enhancing a persistence-unit,
and give the paths to the mapping files and class-files that define the classes being enhanced.
The input to the enhancer should be the name of the "persistence-unit" to enhance. To give an
example of how you would invoke the enhancer
Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jpa.jar:lib/persistence-api.jar:lib/
-Dlog4j.configuration=file:log4j.properties
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu MyUnit
Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jpa.jar;lib\persistence-api.jar;lib\
-Dlog4j.configuration=file:log4j.properties
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu MyUnit
So you pass in the persistence-unit name as the final argument(s) in the list, and include the respective
JAR's in the classpath (-cp). The enhancer responds as follows
If you have errors here relating to "Log4J" then you must fix these first. If you receive no output about
which class was ENHANCED then you should look in the DataNucleus enhancer log for errors. The
enhancer performs much error checking on the validity of the passed MetaData and the majority of
errors are caught at this point. You can also use the DataNucleus Enhancer to check whether classes
are enhanced. To invoke the enhancer in this mode you specify the checkonly flag. This will return
a list of the classes, stating whether each class is enhanced for persistence under JDO or not. The
classes need to be in the CLASSPATH (Please note that a CLASSPATH should contain a set of
JAR's, and a set of directories. It should NOT explictly include class files, and should NOT include
parts of the package names. If in doubt please consult a Java book).
To enable runtime enhancement in other environments, the javaagent option must be set in the java
command line. For example,
java -javaagent:datanucleus-core.jar=-api=JPA Main
The statement above will mean that all classes, when being loaded, will be processed by the
ClassFileTransformer (except class in packages "java.*", "javax.*", "org.datanucleus.*"). This means
that it can be slow since the MetaData search algorithm will be utilised for each. To speed this up
you can specify an argument to that command specifying the names of package(s) that should be
processed (and all others will be ignored). Like this
java -javaagent:datanucleus-core.jar=-api=JPA,mydomain.mypackage1,mydomain.mypackage2 Main
so in this case only classes being loaded that are in mydomain.mypackage1 and
mydomain.mypackage2 will be attempted to be enhanced.
Please take care over the following when using runtime enhancement
• When you have a class with a field of another persistable type make sure that you mark that field
as "persistent" (@Persistent, or in XML) since with runtime enhancement at that point the related
class is likely not yet enhanced so will likely not be marked as persistent otherwise. Be explicit
• If the agent jar is not found make sure it is specified with an absolute path.
import org.datanucleus.enhancer.DataNucleusEnhancer;
This will look in META-INF/persistence.xml and enhance all classes defined by that unit. Please note
that you will need to load the enhanced version of the class into a different ClassLoader after
performing this operation to use them. See this guide
DataNucleus requires that all classes that are persisted implement Persistable. Why should we do
this, Hibernate/TopLink dont need it ?. Well that's a simple question really
• DataNucleus uses this Persistable interface, and adds it using bytecode enhancement techniques
so that you never need to actually change your classes. This means that you get transparent
persistence, and your classes always remain your classes. ORM tools that use a mix of reflection
and/or proxies are not totally transparent.
• DataNucleus' use of Persistable provides transparent change tracking. When any change is
made to an object the change creates a notification to DataNucleus allowing it to be optimally
persisted. ORM tools that dont have access to such change tracking have to use reflection to
detect changes. The performance of this process will break down as soon as you read a large
number of objects, but modify just a handful, with these tools having to compare all object states
for modification at transaction commit time.
Why not also read this comparison of bytecode enhancement, and proxies. It gives a clear enough
comparison of the relative benefits.
In the DataNucleus bytecode enhancement contract there are 3 categories of classes. These are Entity,
PersistenceAware and normal classes. The Meta-Data defines which classes fit into these categories.
To give an example, we have 3 classes. The class A is to be persisted in the datastore. The class B
directly updates the fields of class A but doesn't need persisting. The class C is not involved in the
persistence process. We would define these classes as follows
@Entity
public class A
{
String myField;
...
}
@org.datanucleus.api.jpa.annotations.PersistenceAware
public class B
{
...
}
So our MetaData is mainly for those classes that are Entity (or MappedSuperclass/Embeddable)
and are to be persisted to the datastore. For PersistenceAware classes we simply notate that the
class knows about persistence. We don't define MetaData for any class that has no knowledge of
persistence.
JPA allows implementations to bytecode enhance persistable classes to implement some interface
to provide them with change tracking etc. Users could manually make their classes implement
this Persistable interface but this would impose work on them. JPA permits the use of a byte-code
enhancer that converts the users normal classes to implement this interface. DataNucleus provides its
own byte-code enhancer (in the datanucleus-core.jar). This section describes how to use this enhancer
with DataNucleus.
The example above doesn't show all Persistable methods, but demonstrates that all added methods
and fields are prefixed with "dn" to distinguish them from the users own methods and fields. Also
each persistent field of the class will be given a dnGetXXX, dnSetXXX method so that accesses of
these fields are intercepted so that DataNucleus can manage their "dirty" state.
The MetaData defines which classes are required to be persisted, and also defines which
aspects of persistence each class requires. With JPA all classes are additionally detachable
meaning they can be detached so the class will be enhanced to also implement Detachable
The main thing to know is that the detached state (object id of the datastore object, the version
of the datastore object when it was detached, and which fields were detached is stored in
"dnDetachedState") is stored in the object when it is detached, and available to be merged later on.
If the MetaData is changed in any way during development, the classes should always be
recompiled and re-enhanced afterwards.
• Slows down the code-test cycle. This is erroneous since you only need to enhance just before test
and the provided plugins for Ant, Eclipse and Maven all do the enhancement job automatically
and rapidly.
• Is less "lazy" than the proxy approach since you have to load the object as soon as you get a
pointer to it. In a 1-1 relation you have to load the object then since you would cause issues with
null pointers otherwise. With 1-N relations you load the elements of the collection/map only
when you access them and not the collection/map. Hardly an issue then is it!
• Fail to detect changes to public fields unless you enhance your client code. Firstly very few
people will be writing code with public fields since it is bad practice in an OO design, and
secondly, this is why we have "PersistenceAware" classes.
So as you can see, there are no valid reasons against byte-code enhancement, and the pluses are that
runtime detection of dirty events on objects is much quicker, hence your persistence layer operates
faster without any need for iterative reflection-based checks. The fact is that Hibernate itself also now
has a mode whereby you can do bytecode enhancement although not the default mode of Hibernate.
So maybe it wasn't so evil after all ?
155.2.2 Decompilation
Many people will wonder what actually happens to a class upon bytecode enhancement. In simple
terms the necessary methods and fields are added so as to implement Persistable. If you want to check
this, just use a Java decompiler such as JD. It has a nice GUI allowing you to just select your class to
decompile and shows you the source.
If you want to create the schema ("tables"+"columns"+"constraints") during the persistence process,
the property datanucleus.schema.autoCreateAll provides a way of telling DataNucleus to do this.
It's a shortcut to setting the other 3 properties to true. Thereafter, during calls to DataNucleus to
persist classes or performs queries of persisted data, whenever it encounters a new class to persist that
it has no information about, it will use the MetaData to check the datastore for presence of the "table",
and if it doesn't exist, will create it. In addition it will validate the correctness of the table (compared
to the MetaData for the class), and any other constraints that it requires (to manage any relationships).
If any constraints are missing it will create them.
• If you wanted to only create the "tables" required, and none of the "constraints" the property
datanucleus.schema.autoCreateTables provides this, simply performing the tables part of the
above.
• If you want to create any missing "columns" that are required, the property
datanucleus.schema.autoCreateColumns provides this, validating and adding any missing
columns.
• If you wanted to only create the "constraints" required, and none of the "tables" the property
datanucleus.schema.autoCreateConstraints provides this, simply performing the "constraints"
part of the above.
• If you want to keep your schema fixed (i.e don't allow any modifications at runtime) then make
sure that the properties datanucleus.schema.autoCreate{XXX} are set to false
DataNucleus can check any existing schema against what is implied by the MetaData.
The property datanucleus.schema.validateTables provides a way of telling DataNucleus to validate
any tables that it needs against their current definition in the datastore. If the user already has a
schema, and want to make sure that their tables match what DataNucleus requires (from the MetaData
definition) they would set this property to true. This can be useful for example where you are trying to
map to an existing schema and want to verify that you've got the correct MetaData definition.
The property datanucleus.schema.validateColumns provides a way of telling DataNucleus to
validate any columns of the tables that it needs against their current definition in the datastore. If the
user already has a schema, and want to make sure that their tables match what DataNucleus requires
(from the MetaData definition) they would set this property to true. This will validate the precise
column types and widths etc, including defaultability/nullability settings. Please be aware that many
JDBC drivers contain bugs that return incorrect column detail information and so having this
turned off is sometimes the only option (dependent on the JDBC driver quality).
The property datanucleus.schema.validateConstraints provides a way of telling DataNucleus
to validate any constraints (primary keys, foreign keys, indexes) that it needs against their current
definition in the datastore. If the user already has a schema, and want to make sure that their table
constraints match what DataNucleus requires (from the MetaData definition) they would set this
property to true.
This will mean that all RDBMS DDL and SQL statements will prefix table names with the necessary
catalog and schema names (specify which ones your datastore supports).
Note that the values of the position start at 0, and should be specified completely for all columns of all
fields.
156.2 SchemaTool
DataNucleus SchemaTool currently works with RDBMS, HBase, Excel, OOXML, ODF, MongoDB,
Cassandra datastores and is very simple to operate. It has the following modes of operation :
• createSchema - create the specified schema if the datastore supports that operation.
• deleteSchema - delete the specified schema if the datastore supports that operation.
• create - create all database tables required for the classes defined by the input data.
• delete - delete all database tables required for the classes defined by the input data.
• deletecreate - delete all database tables required for the classes defined by the input data, then
create the tables.
• validate - validate all database tables required for the classes defined by the input data.
• dbinfo - provide detailed information about the database, it's limits and datatypes support. Only
for RDBMS currently.
• schemainfo - provide detailed information about the database schema. Only for RDBMS
currently.
In addition for RDBMS, the create/ delete modes can be used by adding "-ddlFile {filename}" and
this will then not create/delete the schema, but instead output the DDL for the tables/constraints into
the specified file.
For the create, delete and validate modes DataNucleus SchemaTool accepts either of the following
types of input.
• A set of MetaData and class files. The MetaData files define the persistence of the classes they
contain. The class files are provided when the classes have annotations.
• The name of a persistence-unit. The persistence-unit name defines all classes, metadata
files, and jars that make up that unit. Consequently, running DataNucleus SchemaTool with a
persistence unit name will create the schema for all classes that are part of that unit.
Here we provide many different ways to invoke DataNucleus SchemaTool
156.2.1 Maven
If you are using Maven to build your system, you will need the DataNucleus Maven plugin.
This provides 5 goals representing the different modes of DataNucleus SchemaTool. You can
use the goals datanucleus:schema-create, datanucleus:schema-delete, datanucleus:schema-
validate depending on whether you want to create, delete or validate the database tables. To use the
DataNucleus Maven plugin you will may need to set properties for the plugin (in your pom.xml). For
example
<build>
...
<plugins>
<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-maven-plugin</artifactId>
<version>4.0.0-release</version>
<configuration>
<api>JPA</api>
<persistenceUnitName>MyUnit</persistenceUnitName>
<log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
<verbose>true</verbose>
</configuration>
</plugin>
</plugins>
...
</build>
So with these properties when I run SchemaTool it uses properties from the file
datanucleus.properties at the root of the Maven project. I am also specifying a log4j configuration file
defining the logging for the SchemaTool process. I then can invoke any of the Maven goals
156.2.2 Ant
An Ant task is provided for using DataNucleus SchemaTool. It has classname
org.datanucleus.store.schema.SchemaToolTask, and accepts the following parameters
The SchemaTool task extends the Apache Ant Java task, thus all parameters available to the Java task
are also available to the SchemaTool task.
In addition to the parameters that the Ant task accepts, you will need to set up your CLASSPATH
to include the classes and MetaData files, and to define the following system properties via the
sysproperty parameter (not required when specifying the persistence props via the properties file, or
when providing the persistence-unit)
So you could define something like the following, setting up the parameters
schematool.classpath, datanucleus.ConnectionDriverName, datanucleus.ConnectionURL,
datanucleus.ConnectionUserName, and datanucleus.ConnectionPassword to suit your situation.
You define the jdo files to create the tables using fileset.
All classes, MetaData files, "persistence.xml" files must be present in the CLASSPATH. In
terms of the schema to use, you either specify the "props" file (recommended), or you specify the
System properties defining the database connection, or the properties in the "persistence-unit". You
should only specify one of the [modes] above. Let's make a specific example and see the output from
SchemaTool. So we have the following files in our application
So we want to create the schema for our persistent classes. So let's invoke DataNucleus SchemaTool
to do this, from the top level of our project. In this example we're using Linux (change the
CLASSPATH definition to suit for Windows)
So as you see, DataNucleus SchemaTool prints out our input, the properties used, and finally
a success message. If an error occurs, then something will be printed to the screen, and more
information will be written to the log.
package org.datanucleus.store.schema;
So for example to create the schema for classes mydomain.A and mydomain.B you would do
something like this
• Put the javax.validation "validation-api" jar in your CLASSPATH, along with the Bean
Validation implementation jar of your choice
• Set the persistence property javax.persistence.validation.mode to one of auto (default), none, or
callback
• Optionally set the persistence property(s) javax.persistence.validation.group.pre-persist,
javax.persistence.validation.group.pre-update, javax.persistence.validation.group.pre-remove to
fine tune the behaviour (the default is to run validation on pre-persist and pre-update if you don't
specify these).
• Use JPA as you normally would for persisting objects
To give a simple example of what you can do with the Bean Validation API
@Entity
public class Person
{
@Id
@NotNull
private Long id;
@NotNull
@Size(min = 3, max = 80)
private String name;
...
}
So we are validating that instances of the Person class will have an "id" that is not null and that the
"name" field is not null and between 3 and 80 characters. If it doesn't validate then at persist/update
an exception will be thrown. You can add bean validation annotations to classes marked as @Entity,
@MappedSuperclass or @Embeddable.
A further use of the Bean Validation annotations @Size(max=...) and @NotNull is that if you specify
these then you have no need to specify the equivalent JPA attributes since they equate to the same
thing.
158 EntityManagerFactory
.......................................................................................................................................
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
...
So you simply provide the name of the persistence-unit which defines the properties, classes, meta-
data etc to be used. An alternative is to specify the properties to use along with the persistence-unit
name. In that case the passed properties will override any that are specified for the persistence unit
itself.
@PersistenceUnit(unitName="myPU")
EntityManagerFactory emf;
If you want a container-managed EM then you create it by injection like this, providing the name of
the required persistence-unit
@PersistenceContext(unitName="myPU")
EntityManager em;
Please refer to the docs for your JavaEE server for more details.
When designing an application you can usually nicely separate your persistable objects into
independent groupings that can be treated separately, perhaps within a different DAO object, if using
DAOs. JPA introduces the idea of a persistence-unit. A persistence-unit provides a convenient way
of specifying a set of metadata files, and classes, and jars that contain all classes to be persisted in a
grouping. The persistence-unit is named, and the name is used for identifying it. Consequently this
name can then be used when defining what classes are to be enhanced, for example.
To define a persistence-unit you first need to add a file persistence.xml to the META-INF/ directory
of your application jar. This file will be used to define your persistence-units. Let's show an example
</persistence>
In this example we have defined 2 persistence-units. The first has the name "OnlineStore" and
contains 5 classes (annotated). The second has the name "Accounting" and contains a metadata file
called "orm.xml" in a particular package (which will define the classes being part of that unit). This
means that once we have defined this we can reference these persistence-units in our persistence
operations. You can find the XSD for persistence.xml here.
There are several sub-elements of this persistence.xml file
• provider - the JPA persistence provider to be used. The JPA persistence "provider" for
DataNucleus is org.datanucleus.api.jpa.PersistenceProviderImpl
• jta-data-source - JNDI name for JTA connections
• non-jta-data-source - JNDI name for non-JTA connections. Note that if using a JTA datasource
as the primary connection, you ought to provide a non-jta-data-source also since any schema
generation and/or sequence handling will need to use that.
• jar-file - name of a JAR file to scan for annotated classes to include in this persistence-unit.
• mapping-file - name of an XML "mapping" file containing persistence information to be
included in this persistence-unit.
• class - name of an annotated class to include in this persistence-unit
• properties - properties defining the persistence factory to be used. Please refer to Persistence
Properties Guide for details
<persistence-unit name="Store">
<provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
<class>org.datanucleus.samples.metadata.store.Product</class>
<class>org.datanucleus.samples.metadata.store.Book</class>
<class>org.datanucleus.samples.metadata.store.CompactDisc</class>
<exclude-unlisted-classes/>
...
</persistence-unit>
If you don't include the exclude-unlisted-classes then DataNucleus will search for annotated classes
starting at the root of the persistence-unit (the root directory in the CLASSPATH that contains the
"META-INF/persistence.xml" file).
DataNucleus allows an extension to JPA to dynamically create persistence-units at runtime. Use the
following code sample as a guide. Obviously any classes defined in the persistence-unit need to have
been enhanced.
import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.api.jpa.JPAEntityManagerFactory;
It should be noted that if you call pumd.toString(); then this returns the text that would have been
found in a persistence.xml file.
DataNucleus provides many properties to extend the control that JPA gives you. These can be used
alongside the above standard JPA properties, but will only work with DataNucleus. Please consult the
Persistence Properties Guide for full details. In addition we have the following properties explicitly
for JPA.
datanucleus.jpa.addClassTransformer
Description When running with JPA in a JavaEE environment if
you wish to have your classes enhanced at runtime
you can enable this by setting this property to true.
The default is to bytecode enhance your classes
before deployment.
Range of Values false | true
datanucleus.jpa.persistenceContextType
Description JPA defines two lifecycle options. JavaEE usage
defaults to "transaction" where objects are detached
when a transaction is committed. JavaSE usage
defaults to "extended" where objects are detached
when the EntityManager is closed. This property
allows control
Range of Values transaction | extended
datanucleus.jpa.txnMarkForRollbackOnException
Description JPA requires that any persistence exception should
mark the current transaction for rollback. This
persistence property allows that inflexible behaviour
to be turned off leaving it to the user to decide when a
transaction is needing to be rolled back.
Range of Values true | false
159 L2 Cache
.......................................................................................................................................
The Level 2 cache is a DataNucleus plugin point allowing you to provide your own cache where you
require it. Use the examples of the EHCache, Coherence caches etc as reference.
The Cache interface provides methods to control the retention of objects in the cache. You have 2
types of methods
• contains - check if an object of a type with a particular identity is in the cache
• evict - used to remove objects from the Level 2 Cache
You can also control which classes are put into a Level 2 cache. So with the following JPA2
annotation @Cacheable, no objects of type MyClass will be put in the L2 cache.
@Cacheable(false)
@Entity
public class MyClass
{
...
}
If you want to control which fields of an object are put in the Level 2 cache you can do this using an
extension annotation on the field. This setting is only required for fields that are relationships to other
persistable objects. Like this
Collection values;
So in this example we will cache "values" but not "elements". If a field is cacheable then
• If it is a persistable object, the "identity" of the related object will be stored in the Level 2 cache
for this field of this object
• If it is a Collection of persistable elements, the "identity" of the elements will be stored in the
Level 2 cache for this field of this object
• If it is a Map of persistable keys/values, the "identity" of the keys/values will be stored in the
Level 2 cache for this field of this object
When pulling an object in from the Level 2 cache and it has a reference to another object Access
Platform uses the "identity" to find that object in the Level 1 or Level 2 caches to re-relate the objects.
datanucleus.cache.level2.type=javax.cache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.timeout={expiration time in millis - optional}
datanucleus.cache.level2.type=jcache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.timeout={expiration time in millis - optional}
datanucleus.cache.level2.type=coherence
datanucleus.cache.level2.cacheName={coherence cache name}
The Coherence cache name is the name that you would normally put into a call to
CacheFactory.getCache(name). You have the benefits of Coherence's distributed/serialized caching.
If you require more control over the Coherence cache whilst using it with DataNucleus, you can just
access the cache directly via
datanucleus.cache.level2.type=ehcache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.configurationFile={EHCache configuration file (in classpath)}
The EHCache plugin also provides an alternative L2 Cache that is class-based. To use this you would
need to replace "ehcache" above with "ehcacheclassbased".
datanucleus.cache.level2.type=oscache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.type=swarmcache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.type=cacheonix
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.timeout={timeout-in-millis (default=60)}
datanucleus.cache.level2.configurationFile={Cacheonix configuration file (in classpath)}
<?xml version="1.0"?>
<cacheonix>
<local>
<!-- One cache per class being stored. -->
<localCache name="mydomain.MyClass">
<store>
<lru maxElements="1000" maxBytes="1mb"/>
<expiration timeToLive="60s"/>
</store>
</localCache>
<!-- Fallback cache for classes indeterminable from their id. -->
<localCache name="datanucleus">
<store>
<lru maxElements="1000" maxBytes="10mb"/>
<expiration timeToLive="60s"/>
</store>
</localCache>
</cacheonix>
In the case of using container-managed JavaEE, you would instead obtain the EntityManager by
injection
@PersistenceContext(unitName="myPU")
EntityManager em;
In general you will be performing all operations on a EntityManager within a transaction, whether
your transactions are controlled by your JavaEE container, by a framework such as Spring, or by
locally defined transactions. In the examples below we will omit the transaction demarcation for
clarity.
This will result in the object being persisted into the datastore, though clearly it will not be persistent
until you commit the transaction. The LifecycleState of the object changes from Transient to
PersistentClean (after persist()), to Hollow (at commit).
When you want to persist multiple objects with standard JPA you have to call persist multiple times.
Fortunately DataNucleus extends this to take in a Collection or an array of entities, so you can do
em.persist(coll);
As above, the objects are persisted to the datastore. The LifecycleState of the objects change from
Transient to PersistentClean (after persist()), to Hollow (at commit).
where cls is the class of the object you want to find, and id is the identity. Note that the first argument
could be a base class and the real object could be an instance of a subclass of that. Note that the
second argument is either the value of the single primary-key field (when it has only one primary key
field), or is the value of the object-id-class (when it has multiple primary key fields).
When you want to delete multiple objects with standard JPA you have to call remove multiple times.
Fortunately DataNucleus extends this to take in a Collection or an array of entities, so you can do
When you want to attach multiple modified objects with standard JPA you have to call merge
multiple times. Fortunately DataNucleus extends this to take in a Collection or an array of entities, so
you can do
• Refresh all fields that are to be eagerly fetched from the datastore
• Unload all loaded fields that are to be lazily fetched.
If the object had any changes they will be thrown away by this step, and replaced by the latest
datastore values.
JPA doesn't provide a method for getting the EntityManager of an object as such. Fortunately
DataNucleus provides the following
EntityManager em = NucleusJPAHelper.getEntityManager(obj);
• weak - uses a weak reference backing map. If JVM garbage collection clears the reference, then
the object is removed from the cache.
• soft - uses a soft reference backing map. If the map entry value object is not being actively used,
then garbage collection may garbage collect the reference, in which case the object is removed
from the cache.
• strong - uses a normal HashMap backing. With this option all references are strong meaning that
objects stay in the cache until they are explicitly removed by calling remove() on the cache.
You can specify the type of L1 Cache by providing the persistence property
datanucleus.cache.level1.type. You set this to the value of the type required. If you want to remove
all objects from the L1 cache programmatically you should use em.clear() but bear in mind the other
things that this will impact on.
Objects are placed in the L1 Cache (and updated there) during the course of the transaction. This
provides rapid access to the objects in use in the users application and is used to guarantee that there
is only one object with a particular identity at any one time for that EntityManager. When the EM is
closed the cache is cleared.
The L1 cache is a DataNucleus plugin point allowing you to provide your own cache where you
require it.
161.1.1 Transaction
A newly created object is transient. You then persist it and it becomes persistent. You then commit
the transaction and it is detached for use elsewhere in the application, in detached state. You then
attach any changes back to persistence and it becomes persistent again. Finally when you delete the
object from persistence and commit that transaction it is in transient state.
161.1.2 Extended
So a newly created object is transient. You then persist it and it becomes persistent. You
then commit the transaction and it remains managed in persistent state. When you close the
EntityManager it becomes detached. Finally when you delete the object from persistence and commit
that transaction it is in transient state.
161.1.3 Detachment
When you detach an object (and its graph) either explicitly (using em.detach()) or implicitly via the
PersistenceContext above, you need to be careful about which fields are detached. If you detach
everything then you can end up with a huge graph that could impact on the performance of your
application. On the other hand you need to ensure that you have all fields that you will be needing
access to whilst detached. Should you access a field that was not detached an IllegalAccessException
is thrown. All fields that are loaded will be detached so make sure you either load all required when
retrieving the object using Entity Graphs or you access fields whilst attached (which will load them).
Important : Please note that some people interpret the JPA spec as implying that an object which has
a primary key field set to a value as being detached. DataNucleus does not take this point of view,
since the only way you can have a detached object is to detach it from persistence (i.e it was once
managed/attached). To reinforce our view of things, what state is an object in which has a primitive
primary key field ? Using the logic above of these other people any object of such a class would be in
detached state (when not managed) since its PK is set. An object that has a PK field set is transient
unless it was detached from persistence. Note that you can merge a transient object by setting the
persistence property datanucleus.allowAttachOfTransient to true.
Note that DataNucleus does not use the "CascadeType.DETACH" flag explicitly, and instead
detaches the fields that are loaded (or marked for eager loading). In addition it allows the user to make
use of the FetchPlan extension for controlling the fine details of what is loaded (and hence detached).
When an object is detached it is often useful to know which fields are loaded/dirty. You can do this
with the following helper methods
@Entity
public class Account
{
@Id
Long accountId;
Integer balance;
boolean preferred;
@PrePersist
protected void validateCreate()
{
if (getBalance() < MIN_REQUIRED_BALANCE)
{
throw new AccountException("Insufficient balance to open an account");
}
}
@PostLoad
protected void adjustPreferredStatus()
{
preferred = (getBalance() >= AccountManager.getPreferredStatusLevel());
}
}
So in this example just before any "Account" object is persisted the validateCreate method
will be called. In the same way, just after the fields of any "Account" object are loaded the
adjustPreferredStatus method is called. Very simple.
You can register callbacks for the following lifecycle events
• PrePersist
• PostPersist
• PreRemove
• PostRemove
• PreUpdate
• PostUpdate
• PostLoad
The only other rule is that any method marked to be a callback method has to take no arguments as
input, and have void return.
@Entity
@EntityListeners(org.datanucleus.MyEntityListener.class)
public class Account
{
@Id
Long accountId;
Integer balance;
boolean preferred;
So we define our "Account" entity as normal but mark it with an EntityListener, and then in the
EntityListener we define the callbacks we require. As before we can define any of the 7 callbacks as
we require. The only difference is that the callback method has to take an argument of type "Object"
that it will be called for, and have void return.
• EMF : single connection at any one time for datastore-based value generation. Obtained just for
the operation, then released
• EMF : single connection at any one time for schema-generation. Obtained just for the operation,
then released
• EM : single connection at any one time. When in a transaction the connection is held from
the point of retrieval until the transaction commits or rolls back. The exact point at which the
connection is obtained is defined more fully below. When used for non-transactional operations
the connection is obtained just for the specific operation (unless configured to retain it).
If you have multiple threads using the same EntityManager then you can get "ConnectionInUse"
problems where another operation on another thread comes in and tries to perform something while
that first operation is still in use. This happens because the JPA spec requires an implementation to
use a single datastore connection at any one time. When this situation crops up the user ought to use
multiple EntityManagers.
Another important aspect is use of queries for Optimistic transactions, or for non-transactional
contexts. In these situations it isn't possible to keep the datastore connection open indefinitely and so
when the Query is executed the ResultSet is then read into core making the queried objects available
thereafter.
For the datastores supported by DataNucleus, the "native" object is of the following types
• RDBMS : java.sql.Connection
• Excel : org.apache.poi.hssf.usermodel.HSSFWorkbook
• OOXML : org.apache.poi.hssf.usermodel.XSSFWorkbook
• ODF : org.odftoolkit.odfdom.doc.OdfDocument
• LDAP : javax.naming.ldap.LdapContext
• MongoDB : com.mongodb.DB
• HBase : NOT SUPPORTED
• JSON : NOT SUPPORTED
• XML : org.w3c.dom.Document
• NeoDatis : org.neodatis.odb.ODB
• GAE Datastore : com.google.appengine.api.datastore.DatastoreService
• Neo4j : org.neo4j.graphdb.GraphDatabaseService
• Cassandra : com.datastax.driver.core.Session
Things to bear in mind with this connection
• You must return the connection back to the EntityManager before performing any
EntityManager operation. You do this by calling conn.close()
• If you don't return the connection and try to perform an EntityManager operation which requires
the connection then an Exception is thrown.
When you create an EntityManagerFactory using the connection URL, driver name and the
username/password to use, this doesn't necessarily pool the connections. For some of the supported
datastores DataNucleus allows you to utilise a connection pool to efficiently manage the connections
to the datastore. We currently provide support for the following
• RDBMS : DBCP we allow use of externally-defined DBCP, but also provide a builtin DBCP
v1.4
• RDBMS : C3P0
• RDBMS : Proxool
• RDBMS : BoneCP
• RDBMS : HikariCP
• RDBMS : Tomcat
• RDBMS : Manually creating a DataSource for a 3rd party software package
• RDBMS : Custom Connection Pooling Plugins for RDBMS using the DataNucleus
ConnectionPoolFactory interface
• RDBMS : Using JNDI, and lookup a connection DataSource.
• LDAP : Using JNDI
You need to specify the persistence property datanucleus.connectionPoolingType to be whichever
of the external pooling libraries you wish to use (or "None" if you explicitly want no pooling).
DataNucleus provides two sets of connections to the datastore - one for transactional usage, and
one for non-transactional usage. If you want to define a different pooling for nontransactional usage
then you can also specify the persistence property datanucleus.connectionPoolingType.nontx to
whichever is required.
datanucleus.connectionPool.driver.defaultRowPrefetch=50
and it will pass in defaultRowPrefetch as "50" into the driver used by the connection pool.
So the EMF will use connection pooling using DBCP. To do this you will need commons-dbcp,
commons-pool and commons-collections JARs to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for DBCP are shown below
# Pooling of Connections
datanucleus.connectionPool.maxIdle=10
datanucleus.connectionPool.minIdle=3
datanucleus.connectionPool.maxActive=5
datanucleus.connectionPool.maxWait=60
# Pooling of PreparedStatements
datanucleus.connectionPool.maxStatements=0
datanucleus.connectionPool.testSQL=SELECT 1
datanucleus.connectionPool.timeBetweenEvictionRunsMillis=2400000
datanucleus.connectionPool.minEvictableIdleTimeMillis=18000000
So the EMF will use connection pooling using C3P0. To do this you will need the C3P0 JAR to be in
the CLASSPATH. If you want to configure C3P0 further you can include a "c3p0.properties" in your
CLASSPATH - see the C3P0 documentation for details.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for C3P0 are shown below
# Pooling of Connections
datanucleus.connectionPool.maxPoolSize=5
datanucleus.connectionPool.minPoolSize=3
datanucleus.connectionPool.initialPoolSize=3
# Pooling of PreparedStatements
datanucleus.connectionPool.maxStatements=20
So the EMF will use connection pooling using Proxool. To do this you will need the proxool and
commons-logging JARs to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for Proxool are shown below
datanucleus.connectionPool.maxConnections=10
datanucleus.connectionPool.testSQL=SELECT 1
So the EMF will use connection pooling using BoneCP. To do this you will need the BoneCP JAR
(and SLF4J, google-collections) to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for BoneCP are shown below
# Pooling of Connections
datanucleus.connectionPool.maxPoolSize=5
datanucleus.connectionPool.minPoolSize=3
# Pooling of PreparedStatements
datanucleus.connectionPool.maxStatements=20
So the EMF will use connection pooling using HikariCP. To do this you will need the HikariCP JAR
(and SLF4J, javassist as required) to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling. The currently supported
properties for HikariCP are shown below
# Pooling of Connections
datanucleus.connectionPool.maxPoolSize=5
So the EMF will use a DataSource with connection pooling using Tomcat. To do this you will need
the tomcat-jdbc JAR to be in the CLASSPATH.
You can also specify persistence properties to control the actual pooling, like with the other pools.
With DBCP you need to generate a javax.sql.DataSource, which you will then pass to DataNucleus.
You do this as follows
Note that we haven't passed the dbUser and dbPassword to the EMF since we no longer need to
specify them - they are defined for the pool so we let it do the work. As you also see, we set the data
source for the EMF. Thereafter we can sit back and enjoy the performance benefits. Please refer to the
documentation for DBCP for details of its configurability (you will need commons-dbcp, commons-
pool, and commons-collections in your CLASSPATH to use this above example).
Once you have turned connection pooling on if you want more control over the pooling you can also
set the following persistence properties
DataNucleus allows use of a data source that represents the datastore in use. This is often just a URL
defining the location of the datastore, but there are in fact several ways of specifying this data source
depending on the environment in which you are running.
• Nonmanaged Context - Java Client
• Managed Context - Servlet
• Managed Context - JavaEE
If the data source is avaiable in Websphere, the simplest way of using a data source outside the
application server is as follows.
<parameter>
<name>url</name>
<value>jdbc:mysql://127.0.0.1:3306/datanucleus?autoReconnect=true</value>
</parameter>
<parameter>
<name>driverClassName</name>
<value>com.mysql.jdbc.Driver</value>
</parameter>
<parameter>
<name>username</name>
<value>mysql</value>
</parameter>
<parameter>
<name>password</name>
<value></value>
</parameter>
</ResourceParams>
</Context>
With this Tomcat JNDI data source we would then specify the data source (name) as java:comp/env/
jdbc/datanucleus.
164 Transactions
.......................................................................................................................................
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
em.close();
In this case you will have defined your persistence-unit to be like this
or
The basic idea with Locally-Managed transactions is that you are managing the transaction start and
end.
EntityManager em = emf.createEntityManager();
try
{
ut.begin();
ut.commit();
}
finally
{
em.close();
}
so we control the transaction using the UserTransaction. The second scenario is where the
UserTransaction is started after you have the EntityManager. In this case we need to join our
EntityManager to the newly created UserTransaction, like this
EntityManager em = emf.createEntityManager();
try
{
.. perform persistence, query operations
In the JTA case you will have defined your persistence-unit to be like this
164.1.5 No Transactions
DataNucleus allows the ability to operate without transactions. With JPA this is enabled by default
(see the 2 properties datanucleus.NontransactionalRead, datanucleus.NontransactionalWrite
set to true). This means that you can read objects and make updates outside of transactions. This is
effectively an "auto-commit" mode.
EntityManager em = emf.createEntityManager();
em.close();
When using non-transactional operations, you need to pay attention to the persistence property
datanucleus.nontx.atomic. If this is true then any persist/delete/update will be committed to the
datastore immediately. If this is false then any persist/delete/update will be queued up until the next
transaction (or em.close()) and committed with that.
164.1.6 Flushing
During a transaction, depending on the configuration, operations don't necessarily go to the datastore
immediately, often waiting until commit. In some situations you need persists/updates/deletes to be in
the datastore so that subsequent operations can be performed that rely on those being handled first. In
this case you can flush all outstanding changes to the datastore using
em.flush();
A convenient vendor extension is to find which objects are waiting to be flushed at any time, like this
List<ObjectProvider> objs =
((JPAEntityManager)pm).getExecutionContext().getObjectsToBeFlushed();
DataNucleus also allows specification of the transaction isolation level. This is specified via the
EntityManagerFactory property datanucleus.transactionIsolation. It accepts the standard JDBC
values of
• read-uncommitted (1) : dirty reads, non-repeatable reads and phantom reads can occur
• read-committed (2) : dirty reads are prevented; non-repeatable reads and phantom reads can
occur
• repeatable-read (4) : dirty reads and non-repeatable reads are prevented; phantom reads can
occur
• serializable (8) : dirty reads, non-repeatable reads and phantom reads are prevented
The default is read-committed. If the datastore doesn't support a particular isolation level then it
will silently be changed to one that is supported. As an alternative you can also specify it on a per-
transaction basis as follows (using the values in parentheses above).
org.datanucleus.api.jpa.JPAEntityTransaction tx =
(org.datanucleus.api.jpa.JPAEntityTransaction)pm.currentTransaction();
tx.setOption("transaction.isolation", 2);
EntityManager em = emf.createEntityManager();
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.setRollbackOnly();
tx.rollback();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
em.close();
Any call to commit on the transaction will throw an exception forcing the user to roll it back.
• Transactions can lock all records in a datastore and keep them locked until they are ready to
commit their changes. These are known as Pessimistic (or datastore) Locking.
• Transactions can simply assume that things in the datastore will not change until they are ready
to commit, not lock any records and then just before committing make a check for changes. This
is known as Optimistic Locking.
Pessimistic locking isn't directly supported in JPA but are provided as a vendor extension. It is
suitable for short lived operations where no user interaction is taking place and so it is possible to
block access to datastore entities for the duration of the transaction. You would select pessimistic
locking by adding the persistence property datanucleus.Optimistic as false.
By default DataNucleus does not currently lock the objects fetched in pessimistic locking, but
you can configure this behaviour for RDBMS datastores by setting the persistence property
datanucleus.rdbms.useUpdateLock to true. This will result in all "SELECT ... FROM ..." statements
being changed to be "SELECT ... FROM ... FOR UPDATE". This will be applied only where the
underlying RDBMS supports the "FOR UPDATE" syntax.
With pessimistic locking DataNucleus will grab a datastore connection at the first operation, and
maintain it for the duration of the transaction. A single connection is used for the transaction (with the
exception of any Identity Generation operations which need datastore access, so these can use their
own connection).
In terms of the process of pessimistic (datastore) locking, we demonstrate this below.
Here no changes make it to the datastore until the user either commits the transaction, or they invoke
flush(). The impact of this is that when performing a query, by default, the results may not contain
the modified objects unless they are flushed to the datastore before invoking the query. Depending
on whether you need the modified objects to be reflected in the results of the query governs what you
do about that. If you invoke flush() just before running the query the query results will include the
changes. The obvious benefit of optimistic locking is that all changes are made in a block and version
checking of objects is performed before application of changes, hence this mode copes better with
external processes updating the objects.
Please note that for some datastores (e.g RDBMS) the version check followed by update/delete is
performed in a single statement.
See also :-
• "Default Entity Graph" : implicitly defined in all JPA specs, specifying the fetch setting for each
field/property (LAZY/EAGER).
• Named Entity Graphs : a new feature in JPA 2.1 allowing the user to define Named Entity
Graphs in metadata, via annotations or XML
• Unnamed Entity Graphs : a new feature in JPA 2.1 allowing the user to define Entity Graphs via
the JPA API at runtime
class MyClass
{
String name;
HashSet coll;
MyOtherClass other;
}
and we want to have the option of the other field loaded whenever we load objects of this class, we
define our annotations as
@Entity
@NamedEntityGraph(name="includeOther", attributeNodes={@NamedAttributeNode("other")})
public class MyClass
{
...
}
So we have defined an EntityGraph called "includeOther" that just includes the field with name other.
We can retrieve this and then use it in our persistence code, as follows
Here we have made use of the EntityManager.find method and provided the property
javax.persistence.loadgraph to be our EntityGraph. This means that it will fetch all fields in the
default EntityGraph, plus all fields in the includeOther EntityGraph. If we had provided the property
javax.persistence.fetchgraph set to our EntityGraph it would have fetched just the fields defined in
that EntityGraph.
Note that you can also make use of EntityGraphs when using the JPA Query API, specifying the same
properties above but as query hints.
class MyClass
{
String name;
HashSet coll;
MyOtherClass other;
}
and we want to have the option of the other field loaded whenever we load objects of this class, we do
the following
So we have defined an EntityGraph that just includes the field with name other. We can then use this
at runtime in our persistence code, as follows
Here we have made use of the EntityManager.find method and provided the property
javax.persistence.loadgraph to be our EntityGraph. This means that it will fetch all fields in
the default EntityGraph, plus all fields in this EntityGraph. If we had provided the property
javax.persistence.fetchgraph set to our EntityGraph it would have fetched just the fields defined in
that EntityGraph.
Note that you can also make use of EntityGraphs when using the JPA Query API, specifying the same
properties above but as query hints.
• Programmatic Query where the query is defined using the JPA Query API.
• Named Query where the query is defined in MetaData and referred to by its name at runtime(for
JPQL, SQL and Stored Procedures).
Let's now try to understand the Query API in JPA
Query q = em.createQuery("SELECT p FROM Product p WHERE p.param2 < :threshold ORDER BY p.param1 ascending
q.setParameter("threshold", my_threshold);
List results = q.getResultList();
In this Query, we implicitly select JPQL by using the method EntityManager.createQuery(), and
the query is specified to return all objects of type Product (or subclasses) which have the field
param2 less than some threshold value ordering the results by the value of field param1. We've
specified the query like this because we want to pass the threshold value in as a parameter (so maybe
running it once with one value, and once with a different value). We then set the parameter value of
our threshold parameter. The Query is then executed to return a List of results. The example is to
highlight the typical methods specified for a (JPQL) Query.
So we implicitly select SQL by using the method EntityManager.createNativeQuery(), and the query
is specified like in the JPQL case to return all instances of type Product (using the table name in this
SQL query) where the column param2 is less than some threshold value.
Query q = em.createQuery("SELECT p FROM Product p WHERE p.param2 < :threshold ORDER BY p.param1 ascending
q.setFirstResult(1);
q.setMaxResults(3);
so we will get results 1, 2, and 3 returned only. The first result starts at 0 by default.
166.1.4 setHint()
JPA's query API allows implementations to support extensions ("hints") and provides a simple
interface for enabling the use of such extensions on queries.
q.setHint("extension_name", value);
166.1.5 setParameter()
JPA's query API supports named and numbered parameters and provides method for setting the value
of particular parameters. To set a named parameter, for example, you could do
Query q = em.createQuery("SELECT p FROM Product p WHERE p.param2 < :threshold ORDER BY p.param1 ascending
q.setParameter("threshold", value);
Query q = em.createQuery("SELECT p FROM Product p WHERE p.param2 < ?1 ORDER BY p.param1 ascending");
q.setParameter(1, value);
166.1.6 getResultList()
To execute a JPA query you would typically call getResultList. This will return a List of results. This
should not be called when the query is an "UPDATE"/"DELETE".
Query q = em.createQuery("SELECT p FROM Product p WHERE p.param2 < :threshold ORDER BY p.param1 ascending
q.setParameter("threshold", value);
List results = q.getResultList();
166.1.7 getSingleResult()
To execute a JPA query where you are expecting a single value to be returned you would call
getSingleResult. This will return the single Object. If the query returns more than one result then you
will get an Exception. This should not be called when the query is an "UPDATE"/"DELETE".
166.1.8 executeUpdate()
To execute a JPA UPDATE/DELETE query you would call executeUpdate. This will return the
number of objects changed by the call. This should not be called when the query is a "SELECT".
166.1.9 setFlushMode()
By default, when a query is executed it will be evaluated against the contents of the datastore at the
point of execution. If there are any outstanding changes waiting to be flushed then these will not
feature in the results. To make sure all outstanding changes are respected
q.setFlushMode(FlushModeType.AUTO);
166.1.10 setLockMode()
JPA allows control over whether objects found by a fetch (JPQL query) are locked during that
transaction so that other transactions can't update them in the meantime. For example
q.setLockMode(LockModeType.PESSIMISTIC_READ);
You can also specify this for all queries for all EntityManagers using a persistence property
datanucleus.rdbms.useUpdateLock.
JPA doesn't currently define a mechanism for caching of queries. DataNucleus provides 3 levels of
caching
import org.datanucleus.api.jpa.JPAQueryCache;
import org.datanucleus.api.jpa.EntityManagerFactoryImpl;
...
JPAQueryCache cache = ((EntityManagerFactoryImpl)emf).getQueryCache();
cache.evict(query);
which evicts the results of the specific query. The JPAQueryCache has more options available should
you need them ... .
168 JPQL
.......................................................................................................................................
This finds all "Person" objects with surname of "Jones". You specify all details in the query.
SELECT [<result>]
[FROM <candidate-class(es)>]
[WHERE <filter>]
[GROUP BY <grouping>]
[HAVING <having>]
[ORDER BY <ordering>]
The "keywords" in the query are shown in UPPER CASE are case-insensitive.
In strict JPA the entity name cannot be a MappedSuperclass entity name. That is, if you have an
abstract superclass that is persistable, you cannot query for instances of that superclass and its
subclasses. We consider this a significant shortcoming of the querying capability, and allow the
entity name to also be of a MappedSuperclass. You are unlikely to find this supported in other JPA
implementations, but then maybe that's why you chose DataNucleus?
168.1.5 Filter
The most important thing to remember when defining the filter for JPQL is that think how you would
write it in SQL, and its likely the same except for field names instead of column names. The
filter has to be a boolean expression, and can include the candidate entity, fields/properties, literals,
functions, parameters, operators and subqueries
168.1.6 Fields/Properties
In JPQL you refer to fields/properties in the query by referring to the field/bean name. For example,
if you are querying a candidate entity called Product and it has a field "price", then you access it like
this
price < 150.0
Note that if you want to refer to a field/property of an entity you can prefix the field by its alias
p.price < 150.0
You can also chain field references if you have an entity Product (alias = p) with a field of
(persistable) type Inventory, which has a field name, so you could do
p.inventory.name = 'Backup'
168.1.7 Operators
The operators are listed below in order of decreasing precedence.
• Navigation operator (.)
• Arithmetic operators:
• +, - unary
• *, / multiplication and division
• +, - addition and subtraction
• Comparison operators : =, >, >=, <, <=, <> (not equal), [NOT] BETWEEN, [NOT] LIKE,
[NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY, [NOT] MEMBER [OF], [NOT] EXISTS
• Logical operators:
• NOT
• AND
• OR
168.1.8 Literals
JPQL supports literals of the following types : Number, boolean, character, String, NULL and
temporal. When String literals are specified using single-string format they should be surrounded by
single-quotes '. Please note that temporal literals are specified using JDBC escape syntax in String
form, namely
{d 'yyyy-mm-dd'} - a Date
{t 'hh:mm:ss'} - a Time
{ts 'yyyy-mm-dd hh:mm:ss.f...'} - a Timestamp
Named Parameters :
Query q = em.createQuery("SELECT p FROM Person p WHERE p.lastName = :surname AND o.firstName = :forename"
q.setParameter("surname", theSurname);
q.setParameter("forename", theForename");
Numbered Parameters :
Query q = em.createQuery("SELECT p FROM Person p WHERE p.lastName = ?1 AND p.firstName = ?2");
q.setParameter(1, theSurname);
q.setParameter(2, theForename);
So in the first case we have parameters that are prefixed by : (colon) to identify them as a parameter
and we use that name when calling Query.setParameter(). In the second case we have parameters that
are prefixed by ? (question mark) and are numbered starting at 1. We then use the numbered position
when calling Query.setParameter().
Query q = em.createQuery(
"SELECT p.personNum, CASE WHEN p.age < 18 THEN 'Youth' WHEN p.age >= 18 AND p.age < 65 THEN 'Adult' E
So in this case the second result value will be a String, either "Youth", "Adult" or "Old" depending on
the age of the person. The BNF structure of the JPQL CASE expression is
Please note that you can easily add support for other functions for evaluation "in-memory" using this
DataNucleus plugin point
Please note that you can easily add support for other functions with RDBMS datastore using this
DataNucleus plugin point
which will sort primarily by field1 in ascending order, then secondarily by field2 in descending order.
Although it is not (yet) standard JPQL, DataNucleus also supports specifying a directive for where
NULL values of the ordered field/property go in the order, so the full syntax supported is
fieldName [ASC|DESC] [NULLS FIRST|NULLS LAST]
Note that this is only supported for a few RDBMS (H2, HSQLDB, PostgreSQL, DB2, Oracle, Derby,
Firebird, SQLServer v11+).
168.1.13 Subqueries
With JPQL the user has a very flexible query syntax which allows for querying of the vast majority of
data components in a single query. In some situations it is desirable for the query to utilise the results
of a separate query in its calculations. JPQL also allows the use of subqueries. Here's an example
So we want to find all Employees that have a salary greater than the average salary. The subquery
must be in parentheses (brackets). Note that we have defined the subquery with an alias of "f",
whereas in the outer query the alias is "e".
So this returns all employees that earn more than all managers in the same department! You can also
compare with some/any, like this
So this returns all employees that earn more than any one Manager in the same department.
Query q = em.createQuery("SELECT p.firstName, p.lastName FROM Person p WHERE p.age > 20");
List<Object[]> results = q.getResultList();
this returns the first and last name for each Person meeting that filter. Obviously we may have some
container class that we would like the results returned in, so if we change the query to this
Query<PersonName> q = em.createQuery(
"SELECT p.firstName, p.lastName FROM Person p WHERE p.age > 20", PersonName.class);
List<PersonName> results = q.getResultList();
so each result is a PersonName, holding the first and last name. This result class needs to match one of
the following structures
• Constructor taking arguments of the same types and the same order as the result clause. An
instance of the result class is created using this constructor. For example
...
}
• Default constructor, and setters for the different result columns, using the alias name for each
column as the property name of the setter. For example
public PersonName()
{
}
...
}
Note that if the setter property name doesn't match the query result component name, you should use
AS {alias} in the query so they are the same.
If however you know that the query will return multiple results, or you just don't know then you
would call
List results = query.getResultList();
The typical use of a JPQL query is to translate it into the native query language of the datastore and
return objects matched by the query. For many datastores it is simply impossible to support the full
JPQL syntax in the datastore native query language and so it is necessary to evaluate the query in-
memory. This means that we evaluate as much as we can in the datastore and then instantiate those
objects and evaluate further in-memory. Here we document the current capabilities of in-memory
evaluation in DataNucleus.
• Subqueries using ALL, ANY, SOME, EXISTS are not currently supported
• MEMBER OF syntax is not currently supported.
To enable evaluation in memory you specify the query hint datanucleus.query.evaluateInMemory
to true as follows
query.setHint("datanucleus.query.evaluateInMemory","true");
With the JPA API you can either define a query at runtime, or define it in the MetaData/annotations
for a class and refer to it at runtime using a symbolic name. This second option means that the method
of invoking the query at runtime is much simplified. To demonstrate the process, lets say we have a
class called Product (something to sell in a store). We define the JPA Meta-Data for the class in the
normal way, but we also have some query that we know we will require, so we define the following in
the Meta-Data.
<entity class="Product">
...
<named-query name="SoldOut"><![CDATA[
SELECT p FROM Product p WHERE p.status == "Sold Out"
]]></named-query>
</entity>
or using annotations
@Entity
@NamedQuery(name="SoldOut", query="SELECT p FROM Product p WHERE p.status == 'Sold Out'")
public class Product {...}
Note that DataNucleus also supports specifying this using annotations in non-Entity classes.
This is beyond the JPA spec, but is very useful in real applications
So we have a JPQL query called "SoldOut" defined for the class Product that returns all Products (and
subclasses) that have a status of "Sold Out". Out of interest, what we would then do in our application
to execute this query woule be
The "keywords" in the query are shown in UPPER CASE are case-insensitive.
The "keywords" in the query are shown in UPPER CASE are case-insensitive.
Query query = em.createQuery("UPDATE Person p SET p.salary = 10000 WHERE age = 18");
int numRowsUpdated = query.executeUpdate();
CriteriaBuilder cb = emf.getCriteriaBuilder();
CriteriaQuery<Person> crit = cb.createQuery(Person.class);
Root<Person> candidateRoot = crit.from(Person.class);
candidateRoot.alias("p");
crit.select(candidateRoot);
If you ever want to know what is the equivalent JPQL string-based query for your Criteria, just print
out criteriaQuery.toString(). This is not part of the JPA spec, but something that we feel is very
useful so is provided as a DataNucleus vendor extension. So, for example, the criteria query above
would result in the following from crit.toString()
which equates to
SELECT p.name
Note that here we accessed a field by its name (as a String). We could easily have accessed it via the
Criteria MetaModel too.
which equates to
FROM mydomain.Person p JOIN p.address a
String-based:
Predicate nameEquals = cb.equal(candidateRoot.get("name"), "First");
crit.where(nameEquals);
MetaModel-based:
Predicate nameEquals = cb.equal(candidateRoot.get(Person_.name), "First");
crit.where(nameEquals);
You can also invoke methods, so a slight variation on this clause would be
String-based:
Predicate nameUpperEquals = cb.equal(cb.upper(candidateRoot.get("name")), "FIRST");
MetaModel-based:
Predicate nameUpperEquals = cb.equal(cb.upper(candidateRoot.get(Person_.name)), "FIRST");
which equates to
WHERE (UPPER(p.name) = 'FIRST')
String-based:
Order orderFirstName = cb.desc(candidateRoot.get("name"));
crit.orderBy(orderFirstName);
MetaModel-based:
Order orderFirstName = cb.desc(candidateRoot.get(Person_.name));
crit.orderBy(orderFirstName);
which equates to
ORDER BY p.name DESC
String-based:
ParameterExpression param1 = cb.parameter(String.class, "myParam1");
Predicate nameEquals = cb.equal(candidateRoot.get("name"), param1);
crit.where(nameEquals);
MetaModel-based:
ParameterExpression param1 = cb.parameter(String.class, "myParam1");
Predicate nameEquals = cb.equal(candidateRoot.get(Person_.name), param1);
crit.where(nameEquals);
which equates to
WHERE (p.name = :myParam)
Don't forget to set the value of the parameters before executing the query!
String-based:
CriteriaUpdate<Person> crit = qb.createCriteriaUpdate(Person.class);
Root<Person> candidate = crit.from(Person.class);
candidate.alias("p");
crit.set(candidate.get("firstName"), "Freddie");
Predicate teamName = qb.equal(candidate.get("firstName"), "Fred");
crit.where(teamName);
Query q = em.createQuery(crit);
int num = q.executeUpdate();
MetaModel-based:
CriteriaUpdate<Person> crit = qb.createCriteriaUpdate(Person.class);
Root<Person> candidate = crit.from(Person.class);
candidate.alias("p");
crit.set(candidate.get(Person_.firstName), "Freddie");
Predicate teamName = qb.equal(candidate.get(Person.firstName), "Fred");
crit.where(teamName);
Query q = em.createQuery(crit);
int num = q.executeUpdate();
which equates to
UPDATE Person p SET p.firstName = 'Freddie' WHERE p.firstName = 'Fred'
String-based:
CriteriaDelete<Person> crit = qb.createCriteriaDelete(Person.class);
Root<Person> candidate = crit.from(Person.class);
candidate.alias("p");
Predicate teamName = qb.equal(candidate.get("firstName"), "Fred");
crit.where(teamName);
Query q = em.createQuery(crit);
int num = q.executeUpdate();
MetaModel-based:
CriteriaDelete<Person> crit = qb.createCriteriaDelete(Person.class);
Root<Person> candidate = crit.from(Person.class);
candidate.alias("p");
Predicate teamName = qb.equal(candidate.get(Person.firstName), "Fred");
crit.where(teamName);
Query q = em.createQuery(crit);
int num = q.executeUpdate();
which equates to
DELETE FROM Person p WHERE p.firstName = 'Fred'
169.1.11 MetaModel
As we mentioned at the start of this section, there is a MetaModel allowing refactorability. In JPA the
MetaModel is a static metamodel of generated classes that mirror the applications persistable classes
and have persistable fields marked as public and static so that they can be accessed when generating
the queries. In the examples above you saw reference to a class with name with suffix "_". This is a
metamodel class. It is defined below.
The JPA2 spec contains the following description of the static metamodel.
For every managed class in the persistence unit, a corresponding metamodel class is produced as
follows:
• For each managed class X in package p, a metamodel class X_ in package p is created.
• The name of the metamodel class is derived from the name of the managed class by appending
"_" to the name of the managed class.
• The metamodel class X_ must be annotated with the javax.persistence.StaticMetamodel
annotation
• If class X extends another class S, where S is the most derived managed class (i.e., entity or
mapped superclass) extended by X, then class X_ must extend class S_, where S_ is the meta-
model class created for S.
• For every persistent non-collection-valued attribute y declared by class X, where the type of y is
Y, the metamodel class must contain a declaration as follows:
public static volatile SingularAttribute<X, Y> y;
• For every persistent collection-valued attribute z declared by class X, where the element type of
z is Z, the metamodel class must contain a declaration as follows:
• if the collection type of z is java.util.Collection, then
public static volatile CollectionAttribute<X, Z> z;
• if the collection type of z is java.util.Set, then
public static volatile SetAttribute<X, Z> z;
• if the collection type of z is java.util.List, then
public static volatile ListAttribute<X, Z> z;
• if the collection type of z is java.util.Map, then
public static volatile MapAttribute<X, K, Z> z;
where K is the type of the key of the map in class X
Let's take an example, for the following class
package org.datanucleus.samples.jpa2.metamodel;
import java.util.*;
import javax.persistence.*;
@Entity
public class Person
{
@Id
long id;
String name;
@OneToMany
List<Address> addresses;
}
package org.datanucleus.samples.jpa2.metamodel;
import javax.persistence.metamodel.*;
@StaticMetamodel(Person.class)
public class Person_
{
public static volatile SingularAttribute<Person, Long> id;
public static volatile SingularAttribute<Person, String> name;
public static volatile ListAttribute<Person, Address> addresses;
}
So how do we generate this metamodel definition for our query classes? DataNucleus provides an
annotation processor in the jar datanucleus-jpa-query that can be used when compiling your model
classes to generate the static metamodel classes. What this does is when the compile is invoked, all
classes that have persistence annotations will be passed to the annotation processor and a Java file
generated for its metamodel. Then all classes (original + metamodel) are compiled.
To enable this in Maven2 you would need the above jar, plus persistence-api.jar to be in the
CLASSPATH at compile
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
• Go to Java Compiler and make sure the compiler compliance level is 1.7 or above (needed for
DN 4.0+ anyway)
• Go to Java Compiler -> Annotation Processing and enable the project specific settings and
enable annotation processing
• Go to Java Compiler -> Annotation Processing -> Factory Path, enable the project specific
settings and then add the following jars to the list: datanucleus-jpa-query.jar, persistence-api.jar
Query q = em.createNativeQuery("SELECT p.id, o.firstName, o.lastName FROM Person p, Job j WHERE (p.job =
List results = (List)q.getResultsList();
This finds all "Person" objects that do the job of "Cleaner". The syntax chosen has to be runnable
on the RDBMS that you are using (and since SQL is anything but "standard" you will likely have to
change your query when moving to another datastore).
Numbered Parameters :
Query q = em.createQuery("SELECT p FROM Person p WHERE p.lastName = ?1 AND p.firstName = ?2");
q.setParameter(1, theSurname).setParameter(2, theForename);
So we have parameters that are prefixed by ? (question mark) and are numbered starting at 1. We
then use the numbered position when calling Query.setParameter(). This is known as numbered
parameters. With JPA native queries we can't use named parameters officially.
DataNucleus also actually supports use of named parameters where you assign names just like in
JPQL. This is not defined by the JPA specification so dont expect other JPA implementations to
support it. Let's take the previous example and rewrite it using named parameters, like this
Named Parameters :
Query q = em.createQuery("SELECT p FROM Person p WHERE p.lastName = :firstParam AND p.firstName = :otherP
q.setParameter("firstParam", theSurname).setParameter("otherParam", theForename);
If however you know that the query will return multiple results, or you just don't know then you
would call
List results = query.getResultList();
@Entity
@Table(name="LOGIN")
public class Login
{
@Id
private long id;
@Entity
@Table(name="LOGINACCOUNT")
public class LoginAccount
{
@Id
private long id;
The first thing to do is to select both LOGIN and LOGINACCOUNT columns in a single call, and
return instances of the 2 entities. So we define the following in the LoginAccount class
@SqlResultSetMappings({
@SqlResultSetMapping(name="LOGIN_PLUS_ACCOUNT",
entities={@EntityResult(entityClass=LoginAccount.class), @EntityResult(entityClass=Login.class)})
Next thing to try is the same as above, returning 2 entities for a row, but here we explicitly define the
mapping of SQL column to the constructor parameter.
@SqlResultSetMapping(name="AN_LOGIN_PLUS_ACCOUNT_ALIAS", entities={
@EntityResult(entityClass=LoginAccount.class, fields={@FieldResult(name="id", column="THISID"
@EntityResult(entityClass=Login.class, fields={@FieldResult(name="id", column="IDLOGIN"), @Fi
})
For our final example we will return each row as a non-entity class, defining how the columns map to
the constructor for the result class.
@SqlResultSetMapping(name="AN_LOGIN_PLUS_ACCOUNT_CONSTRUCTOR", classes={
@ConstructorResult(targetClass=LoginAccountComplete.class,
columns={@ColumnResult(name="FN"), @ColumnResult(name="LN"), @ColumnResult(name="USER"), @
})
List result = em.createNativeQuery("SELECT P.FIRSTNAME AS FN, P.LASTNAME AS LN, L.USERNAME AS USER, L.PAS
"JPA_AN_LOGINACCOUNT P, JPA_AN_LOGIN L","AN_LOGIN_PLUS_ACCOUNT_CONSTRUCTOR").getResultList();
Iterator iter = result.iterator();
while (iter.hasNext())
{
LoginAccountComplete acctCmp = (LoginAccountComplete)iter.next();
...
}
With the JPA API you can either define a query at runtime, or define it in the MetaData/annotations
for a class and refer to it at runtime using a symbolic name. This second option means that the method
of invoking the query at runtime is much simplified. To demonstrate the process, lets say we have a
class called Product (something to sell in a store). We define the JPA Meta-Data for the class in the
normal way, but we also have some query that we know we will require, so we define the following in
the Meta-Data.
<entity class="Product">
...
<named-native-query name="PriceBelowValue"><![CDATA[
SELECT NAME FROM PRODUCT WHERE PRICE < ?1
]]></named-native-query>
</entity>
or using annotations
@Entity
@NamedNativeQuery(name="PriceBelowValue", query="SELECT NAME FROM PRODUCT WHERE PRICE < ?1")
public class Product {...}
So here we have a native query that will return the names of all Products that have a price less than
a specified value. This leaves us the flexibility to specify the value at runtime. So here we run our
named native query, asking for the names of all Products with price below 20 euros.
If we have any parameters in this stored procedure we need to register them, for example
If you have any result class, or result set mapping then you can specify those in the
createStoredProcedureQuery call. Now we are ready to execute the query and access the results.
spq.execute();
Object paramVal = spq.getOutputParameterValue("PARAM2");
or you can also access the output parameters via position (if specified by position).
boolean isResultSet = spq.execute(); // returns true when we have a result set from the proc
List results1 = spq.getResultList(); // get the first result set
if (spq.hasMoreResults())
{
List results2 = spq.getResultList(); // get the second result set
}
So the user can get hold of multiple result sets returned by their stored procedure.
@NamedStoredProcedureQuery(name="myTestProc", procedureName="MY_TEST_SP_1",
parameters={@StoredProcedureParameter(name="PARAM1", type=String.class, mode=ParameterMode.IN})
@Entity
public class MyClass {...}
172 Guides
.......................................................................................................................................
• Datastore Replication
• JavaEE Environments
• OSGi Environments
• Security
• Troubleshooting
• Performance Tuning
• Monitoring
• Logging
• Maven with DataNucleus
• Eclipse with DataNucleus
• IDEA with DataNucleus
• Netbeans with DataNucleus
• Tutorial with RDBMS
• Tutorial with ODF
• Tutorial with Excel
• Tutorial with MongoDB
• Tutorial with HBase
• Tutorial with Neo4J
• Tutorial with Cassandra
• Eclipse Dali
• TomEE and DataNucleus
• JPA Tutorial (TheServerSide)
Many applications make use of multiple datastores. It is a common requirement to be able to replicate
parts of one datastore in another datastore. Obviously, depending on the datastore, you could make
use of the datastores own capabilities for replication. DataNucleus provides its own extension to
JPA to allow replication from one datastore to another. This extension doesn't restrict you to using 2
datastores of the same type. You could replicate from RDBMS to XML for example, or from MySQL
to HSQLDB.
You need to make sure you have the persistence property datanucleus.attachSameDatastore set
to false if using replication
Note that the case of replication between two RDBMS of the same type is usually way more
efficiently replicated using the capabilities of the datastore itself
The following sample code will replicate all objects of type Product and Employee from EMF1 to
EMF2. These EMFs are created in the normal way so, as mentioned above, EMF1 could be for a
MySQL datastore, and EMF2 for XML. By default this will replicate the complete object graphs
reachable from these specified types.
import org.datanucleus.api.jpa.JPAReplicationManager;
...
• JBoss 7.1
so we have provided a class loader for the OSGi context of the application, and also specified that we
want to use the OSGiPluginRegistry.
All persistence and query operations using EntityManager etc thereafter are identical to what you
would use in a normal JSE/JEE application.
The pom.xml also defines the imports/exports for our OSGi application bundle, so look at this if
wanting guidance on what these could look like when using Maven and the "felix bundle" plugin.
If you read the file README.txt you can see basic instructions on how to deploy this application
into a fresh download of Apache Karaf, and run it. It makes uses of Spring DM to start the JPA
"application".
176.1.1 Enhancement
You should perform enhancement before runtime. That is, do not use java agent since it will enhance
classes at runtime, when you want responsiveness from your application.
#schema creation
datanucleus.schema.autoCreateAll=false
datanucleus.schema.autoCreateTables=false
datanucleus.schema.autoCreateColumns=false
datanucleus.schema.autoCreateConstraints=false
#schema validation
datanucleus.schema.validateTables=false
datanucleus.schema.validateConstraints=false
datanucleus.schema.validateColumns=false
datanucleus.rdbms.CheckExistTablesOrViews=false
datanucleus.rdbms.initializeColumnInfo=None
DataNucleus has 2 ways of handling calls to SCO Collections/Maps. The original method was to
pass all calls through to the datastore. The second method (which is now the default) is to cache
the collection/map elements/keys/values. This second method will read the elements/keys/values
once only and thereafter use the internally cached values. This second method gives significant
performance gains relative to the original method. You can configure the handling of collections/maps
as follows :-
• Globally for the EMF - this is controlled by setting the persistence property
datanucleus.cache.collections. Set it to true for caching the collections (default), and false to
pass through to the datastore.
• For the specific Collection/Map - this overrides the global setting and is controlled by adding
a MetaData <collection> or <map> extension cache. Set it to true to cache the collection data,
and false to pass through to the datastore.
The second method also allows a finer degree of control. This allows the use of lazy loading of data,
hence elements will only be loaded if they are needed. You can configure this as follows :-
• Globally for the EMF - this is controlled by setting the property
datanucleus.cache.collections.lazy. Set it to true to use lazy loading, and set it to false to load
the elements when the collection/map is initialised.
• For the specific Collection/Map - this overrides the global EMF setting and is controlled by
adding a MetaData <collection> or <map> extension cache-lazy-loading. Set it to true to use
lazy loading, and false to load once at initialisation.
• Use datanucleus.RetainValues=true. This is the default for JPA operation and will ensure that
after commit the fields of the object retain their values (rather than being nulled).
• Use detach method.
em.getTransaction().commit();
}
finally
{
em.close();
}
//read or change the detached object here
System.out.prinln(copy.getName());
The bottom line is to not use detachment if instances will only be used to read values.
176.1.15 Logging
I/O consumes a huge slice of the total processing time. Therefore it is recommended to reduce or
disable logging in production. To disable the logging set the DataNucleus category to OFF in the
Log4j configuration. See Logging for more information.
log4j.category.DataNucleus=OFF
What is a benchmark? This is simply a series of persistence operations performing particular things e.g
persist n objects, or retrieve n objects. If those operations are representative of your application then the
benchmark is valid to you.
To find (or create) a benchmark appropriate to your project you need to determine the typical
persistence operations that your application will perform. Are you interested in persisting 100 objects
at once, or 1 million, for example? Then when you have a benchmark appropriate for that operation,
compare the persistence solutions.
The performance tuning guide above gives a good oversight of tuning capabilities, and also refer to
the following blog entry for our take on performance of DataNucleus AccessPlatform. And then the
later blog entry about how to tune for bulk operations
• It is essential that tests for such as Hibernate and DataNucleus performance comparable things.
Some of the original tests had the "delete" simply doing a "DELETE FROM TBL" for Hibernate
yet doing an Extent followed by delete each object individually for a JDO implementation.
This is an unfair comparison and in the source tree in JPOX SVN this is corrected. This fix was
pointed out to the PolePos SourceForge project but is not, as yet, fixed
• It is essential that schema is generated before the test, otherwise the test is no longer a
benchmark of just a persistence operation. The source tree in JPOX SVN assumes the schema
exists. This fix was pointed out to the PolePos SourceForge project but is not, as yet, fixed
• Each persistence implementation should have its own tuning options, and be able to add things
like discriminators since that is what would happen in a real application. The source tree in JPOX
SVN does this for JPOX running. Similarly a JDO implementation would tune the fetch groups
being used - this is not present in the SourceForge project but is in JPOX SVN.
• DataNucleus performance is considered to be significantly improved over JPOX particularly due
to batched inserts, and due to a rewritten query implementation that does enhanced fetching.
177 Troubleshooting
.......................................................................................................................................
177.2.1 Introdution
Java allocate objects in the runtime memory data area called heap. The heap is created on virtual
machine start-up. The memory allocated to objects are reclaimed by Garbage Collectors when the
object is no longer referenced (See Object References). The heap may be of a fixed size, but can also
be expanded when more memory is needed or contracted when no longer needed. If a larger heap is
needed and it cannot be allocated an OutOfMemory is thrown. See JVM Specification.
Native memory is used by the JVM to perform its operations like creation of threads, sockets, jdbc
drivers using native code, libraries using native code, etc.
The maximum size of heap memory is determined by the -Xmx on the java command line. If Xmx is
not set, then the JVM decides for the maximum heap. The heap and native memory are limited to the
maximum memory allocated by the JVM. For example, if the JVM Xmx is set to 1GB and currently
use of native memory is 256MB then the heap can only use 768MB.
177.2.2 Causes
Common causes of out of memory:
• Not enough heap - The JVM needs more memory to deal with the application requirements.
Queries returning more objects than usual can be the cause.
• Not enough PermGen - The JVM needs more memory to load class definitions.
• Memory Leaks - The application does not close the resources, like the EntityManager or Queries,
and the JVM cannot reclaim the memory.
• Caching - Caching in the application or inside DataNucleus holding strong references to objects.
• Garbage Collection - If no full garbage collection is performed before the OutOfMemory it can
indicate a bug in the JVM Garbage Collector.
• Memory Fragmentation - A large object needs to be placed in the memory, but the JVM cannot
allocate a continous space to it because the memory is fragmented.
• JDBC driver - a bug in the JDBC driver not flushing resources or keeping large result sets in
memory.
177.2.3 Throubleshooting
177.2.3.1 JVM
Collect garbage collection information by adding -verbosegc to the java command line. The
verbosegc flag will print garbage collections to System output.
177.2.3.3 DataNucleus
DataNucleus keeps in cache persistent objects using weak references by default. Enable debug mode
DataNucleus.Cache category to investigate the size of the cache in DataNucleus.
177.2.4 Resolution
DataNucleus can be configured to reduce the number of objects in cache. DataNucleus has cache for
persistent objects, metadata, datastore metadata, fields of type Collection or Map, or query results.
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try
{
tx.begin();
//...
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try
{
tx.begin();
for (int i=0; i<100000; i++)
{
Wardrobe wardrobe = new Wardrobe();
wardrobe.setModel("3 doors");
pm.makePersistent(wardrobe);
if (i % 10000 == 0)
{
em.flush();
}
}
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
177.3.1 Introdution
The application pauses for short or long periods or hangs during very long time.
177.3.2 Causes
Common causes:
• Database Locking - Database waiting other transactions to release locks due to deadlock or
locking contentions.
• Garbage Collection Pauses - The garbage collection pauses the application to free memory
resources.
• Application Locking - Thread 2 waiting for resources locked by Thread 1.
177.3.3 Throubleshooting
177.4 Postgres
177.4.1.1 Problem
Exception org.postgresql.util.PSQLException: ERROR: schema "PUBLIC" does not exist raised
during transaction.
177.4.1.2 Troubleshooting
• Verify that the schema "PUBLIC" exists. If the name is lowercased ("public"), set
datanucleus.identifier.case=PreserveCase, since Postgres is case sensitive.
• Via pgAdmin Postgres tool, open a connection to the schema and verify it is acessible with
issuing a SELECT 1 statement.
177.5.1.1 Problem
CreateProcess error=87 when running DataNucleus tools under Microsoft Windows OS.
Windows has an (antiquated) command line length limitation, between 8K and 64K characters
depending on the Windows version, that may be triggered when running tools such as the Enhancer or
the SchemaTool with too many arguments.
177.5.1.2 Solution
When running such tools from Maven or Ant, disable the fork mechanism by setting the option
fork="false".
178 Monitoring
.......................................................................................................................................
Once you start your application and DataNucleus is initialized you can browse DataNucleus MBeans
using a tool called jconsole (jconsole is distributed with the Sun JDK) via the URL:
service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi
Note that the mode of usage is presented in this document as matter of example, and by no means we
recommend to disable authentication and secured communication channels. Further details on the Sun
JMX implementation and how to configure it properly can be found in here.
DataNucleus MBeans are registered in a MBean Server when DataNucleus is started up (e.g. upon
JPA EMF instantiation). To see the full list of DataNucleus MBeans, refer to the javadocs.
To enable management using MX4J you must specify the persistence property datanucleus.jmxType
as mx4j when creating the EMF, and have the mx4j and mx4j-tools jars in the CLASSPATH.
<project>
...
<dependencies>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
...
</project>
The only distinction to make here is that the above is for compile time since your persistence code (if
implementation independent) will only depend on the basic persistence API. At runtime you will need
the DataNucleus artifacts present also, so this becomes
<project>
...
<dependencies>
...
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-core</artifactId>
<version>(3.9, )</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-api-jpa</artifactId>
<version>(3.9, )</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-rdbms</artifactId>
<version>(3.9, )</version>
<scope>runtime</scope>
</dependency>
</dependencies>
...
</project>
Obviously replace the datanucleus-rdbms jar with the jar for whichever datastore you are using. If
you are running the Maven "exec" plugin you may not need the "runtime" specifications.
Please note that you can alternatively use the convenience artifact for JPA+RDBMS (when using
RDBMS).
<project>
...
<dependencies>
...
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-accessplatform-jpa-rdbms</artifactId>
<version>4.0.0-release</version>
<type>pom</type>
</dependency>
</dependencies>
...
</project>
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-maven-plugin</artifactId>
<version>4.0.0-release</version>
<configuration>
<api>JPA</api>
<persistenceUnitName>MyUnit</persistenceUnitName>
<log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Note that this plugin step will automatically try to bring in the latest applicable version of
datanucleus-core for use by the enhancer. It does this since you don't need to have datanucleus-core
in your POM for compilation/enhancement. If you want to use an earlier version then you need to add
exclusions to the maven-datanucleus-plugin
The executions part of that will make enhancement be performed immediately after compile, so
automatic. See also the Enhancer docs
To run the enhancer manually you do
mvn datanucleus:enhance
mvn datanucleus:schema-create
Below this you can set the location of a configuration file for Log4j to use. This is useful when you
want to debug the Enhancer/SchemaTool operations.
• Input file extensions : the enhancer accepts input defining the classes to be enhanced. With JPA
you will typically just specify the "persistence-unit" and list the classes and mapping files in
there. You can alternatively specify the suffices of files that define what will be enhanced (e.g
"class" for annotated classes, and "xml" for the ORM mapping file defining entities)
• Verbose : selecting this means you get much more output from the enhancer
• PersistenceUnit : Name of the persistence unit if enhancing a persistence-unit
• Input file extensions : SchemaTool accepts input defining the classes to have their schema
generated. As for the enhancer, you can run this from a "persistence-unit"
• Verbose : selecting this means you get much more output from SchemaTool
• PersistenceUnit : Name of the persistence unit if running SchemaTool on a persistence-unit
• Datastore details : You can either specify the location of a properties file defining the location
of your datastore, or you supply the driver name, URL, username and password.
Messages from the SchemaTool process will be written to the Eclipse Console.
181.1.1 Background
An application can be JPA-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the Dali Eclipse plugin coupled with the DataNucleus Eclipse
plugin. Alternatively the project could use Ant, Maven2 or some other build tool. In this case this
tutorial should be used as a guiding way for using DataNucleus in the application. The JPA process is
quite straightforward.
181.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Inventory
{
@Id
String name = null;
package org.datanucleus.samples.jpa.tutorial;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Product
{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
long id;
...
}
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @Entity and their primary key field(s)
with @Id In addition we defined a valueStrategy for Product field id so that it will have its values
generated automatically. In this tutorial we are using application identity which means that all objects
of these classes will have their identity defined by the primary key field(s). You can read more in
application identity when designing your systems persistence.
src/main/java/org/datanucleus/samples/jpa/tutorial/Book.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jpa/tutorial/Book.class
target/classes/org/datanucleus/samples/jpa/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jpa/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jpa.jar:lib/persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jpa.jar;lib\persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
This command enhances all classes defined in the persistence-unit "Tutorial". If you accidentally
omitted this step, at the point of running your application and trying to persist an object, you would
get a ClassNotPersistableException thrown. The use of the enhancer is documented in more detail in
the Enhancer Guide. The output of this step are a set of class files that represent persistable classes.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Please note that the finally step is important in that it tidies up connections to the datastore and the
EntityManager.
Now we want to retrieve some objects from persistent storage, so we will use a "Query". In our case
we want access to all Product objects that have a price below 150.00 and ordering them in ascending
order.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JPA book will provide many examples.
Using Ant (you need the included persistence.xml to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/persistence-api.jar:lib/datanucleus-core.jar:lib/datanucleus-rdbms.jar:
lib/datanucleus-api-jpa.jar:lib/{jdbc-driver}.jar:target/classes/:.
org.datanucleus.samples.jpa.tutorial.Main
Manually on Windows :
java -cp lib\persistence-api.jar;lib\datanucleus-core.jar;lib\datanucleus-rdbms.jar;
lib\datanucleus-api-jpa.jar;lib\{jdbc-driver}.jar;target\classes\;.
org.datanucleus.samples.jpa.tutorial.Main
Output :
End of Tutorial
In the above simple tutorial we showed how to employ JPA and persist objects to an RDBMS.
Obviously this just scratches the surface of what you can do, and to use JPA requires minimal work
from the user. In this second part we show some further things that you are likely to want to do.
181.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the DataNucleus SchemaTool to generate the tables where these domain
objects will be persisted. DataNucleus RDBMS SchemaTool is a command line utility (it can be
invoked from Maven/Ant in a similar way to how the Enhancer is invoked). The first thing that you
need is to update the src/main/resources/META-INF/persistence.xml file with your database details.
Here we have a sample file (for HSQLDB) that contains
</persistence>
Now we need to run DataNucleus RDBMS SchemaTool. For our case above you would do something
like this
Using Ant :
ant createschema
Using Maven :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/persistence-api.jar:lib/datanucleus-core.jar:
lib/datanucleus-rdbms.jar:lib/datanucleus-api-jpa.jar:lib/{jdbc_driver.jar}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\persistence-api.jar;lib\datanucleus-core.jar;
lib\datanucleus-rdbms.jar;lib\datanucleus-api-jpa.jar;lib\{jdbc_driver.jar}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
This will generate the required tables, indexes, and foreign keys for the classes defined in the
annotations and orm.xml Meta-Data file.
182.1.1 Background
An application can be JPA-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the Dali Eclipse plugin coupled with the DataNucleus Eclipse
plugin. Alternatively the project could use Ant, Maven2 or some other build tool. In this case this
tutorial should be used as a guiding way for using DataNucleus in the application. The JPA process is
quite straightforward.
182.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Inventory
{
@Id
String name = null;
package org.datanucleus.samples.jpa.tutorial;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Product
{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
long id;
...
}
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @Entity and their primary key field(s)
with @Id In addition we defined a valueStrategy for Product field id so that it will have its values
generated automatically. In this tutorial we are using application identity which means that all objects
of these classes will have their identity defined by the primary key field(s). You can read more in
application identity when designing your systems persistence.
src/main/java/org/datanucleus/samples/jpa/tutorial/Book.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jpa/tutorial/Book.class
target/classes/org/datanucleus/samples/jpa/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jpa/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jpa.jar:lib/persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jpa.jar;lib\persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
This command enhances all class files specified in the persistence-unit "Tutorial". If you accidentally
omitted this step, at the point of running your application and trying to persist an object, you would
get a ClassNotPersistableException thrown. The use of the enhancer is documented in more detail in
the Enhancer Guide. The output of this step are a set of class files that represent persistable classes.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Please note that the finally step is important in that it tidies up connections to the datastore and the
EntityManager.
Now we want to retrieve some objects from persistent storage, so we will use a "Query". In our case
we want access to all Product objects that have a price below 150.00 and ordering them in ascending
order.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JPA book will provide many examples.
Using Ant (you need the included persistence.xml to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/persistence-api.jar:lib/datanucleus-core.jar:lib/datanucleus-odf.jar:
lib/datanucleus-api-jpa.jar:lib/odfdom.jar:target/classes/:.
org.datanucleus.samples.jpa.tutorial.Main
Manually on Windows :
java -cp lib\persistence-api.jar;lib\datanucleus-core.jar;lib\datanucleus-odf.jar;
lib\datanucleus-api-jpa.jar;lib\odfdom.jar;target\classes\;.
org.datanucleus.samples.jpa.tutorial.Main
Output :
End of Tutorial
In the above simple tutorial we showed how to employ JPA and persist objects to an ODF
spreadsheet. Obviously this just scratches the surface of what you can do, and to use JPA requires
minimal work from the user. In this second part we show some further things that you are likely to
want to do.
This file should be placed at the root of the CLASSPATH under META-INF.
182.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the DataNucleus SchemaTool to generate the tables where these domain
objects will be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked
from Maven/Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to
update the src/java/META-INF/persistence.xml file with your database details. Here we have a sample
file (for HSQLDB) that contains
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/persistence-api.jar:lib/datanucleus-core.jar:
lib/datanucleus-odf.jar:lib/datanucleus-api-jpa.jar:lib/{odfdom.jar}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\persistence-api.jar;lib\datanucleus-core.jar;
lib\datanucleus-odf.jar;lib\datanucleus-api-jpa.jar;lib\{odfdom.jar}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
This will generate the required tables, indexes, and foreign keys for the classes defined in the
annotations and orm.xml Meta-Data file.
183.1.1 Background
An application can be JPA-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the Dali Eclipse plugin coupled with the DataNucleus Eclipse
plugin. Alternatively the project could use Ant, Maven2 or some other build tool. In this case this
tutorial should be used as a guiding way for using DataNucleus in the application. The JPA process is
quite straightforward.
183.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Inventory
{
@Id
String name = null;
package org.datanucleus.samples.jpa.tutorial;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Product
{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
long id;
...
}
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @Entity and their primary key field(s)
with @Id In addition we defined a valueStrategy for Product field id so that it will have its values
generated automatically. In this tutorial we are using application identity which means that all objects
of these classes will have their identity defined by the primary key field(s). You can read more in
application identity when designing your systems persistence.
src/main/java/org/datanucleus/samples/jpa/tutorial/Book.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jpa/tutorial/Book.class
target/classes/org/datanucleus/samples/jpa/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jpa/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jpa.jar:lib/persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jpa.jar;lib\persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
This command enhances all class files specified in the persistence-unit "Tutorial". If you accidentally
omitted this step, at the point of running your application and trying to persist an object, you would
get a ClassNotPersistableException thrown. The use of the enhancer is documented in more detail in
the Enhancer Guide. The output of this step are a set of class files that represent persistable classes.
So we created an EntityManagerFactory for our "persistence-unit" called "Tutorial". Now that the
application has an EntityManager it can persist objects. This is performed as follows
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Please note that the finally step is important in that it tidies up connections to the datastore and the
EntityManager.
Now we want to retrieve some objects from persistent storage, so we will use a "Query". In our case
we want access to all Product objects that have a price below 150.00 and ordering them in ascending
order.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JPA book will provide many examples.
Using Ant (you need the included persistence.xml to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/persistence-api.jar:lib/datanucleus-core.jar:lib/datanucleus-excel.jar:
lib/datanucleus-api-jpa.jar:lib/{poi_jars}:target/classes/:.
org.datanucleus.samples.jpa.tutorial.Main
Manually on Windows :
java -cp lib\persistence-api.jar;lib\datanucleus-core.jar;lib\datanucleus-excel.jar;
lib\datanucleus-api-jpa.jar;lib\{poi_jars};target\classes\;.
org.datanucleus.samples.jpa.tutorial.Main
Output :
End of Tutorial
In the above simple tutorial we showed how to employ JPA and persist objects to an Excel
spreadsheet. Obviously this just scratches the surface of what you can do, and to use JPA requires
minimal work from the user. In this second part we show some further things that you are likely to
want to do.
This file should be placed at the root of the CLASSPATH under META-INF.
183.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the DataNucleus SchemaTool to generate the tables where these domain
objects will be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked
from Maven/Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to
update the src/java/META-INF/persistence.xml file with your database details. Here we have a sample
file (for HSQLDB) that contains
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/persistence-api.jar:lib/datanucleus-core.jar:
lib/datanucleus-excel.jar:lib/datanucleus-api-jpa.jar:lib/{poi.jar}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\persistence-api.jar;lib\datanucleus-core.jar;
lib\datanucleus-excel.jar;lib\datanucleus-api-jpa.jar;lib\{poi.jar}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
This will generate the required tables, indexes, and foreign keys for the classes defined in the
annotations and orm.xml Meta-Data file.
184.1.1 Background
An application can be JPA-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the Dali Eclipse plugin coupled with the DataNucleus Eclipse
plugin. Alternatively the project could use Ant, Maven2 or some other build tool. In this case this
tutorial should be used as a guiding way for using DataNucleus in the application. The JPA process is
quite straightforward.
184.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Inventory
{
@Id
String name = null;
package org.datanucleus.samples.jpa.tutorial;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Product
{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
long id;
...
}
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @Entity and their primary key field(s)
with @Id In addition we defined a valueStrategy for Product field id so that it will have its values
generated automatically. In this tutorial we are using application identity which means that all objects
of these classes will have their identity defined by the primary key field(s). You can read more in
application identity when designing your systems persistence.
src/java/org/datanucleus/samples/jpa/tutorial/Book.java
src/java/org/datanucleus/samples/jpa/tutorial/Inventory.java
src/java/org/datanucleus/samples/jpa/tutorial/Product.java
target/classes/org/datanucleus/samples/jpa/tutorial/Book.class
target/classes/org/datanucleus/samples/jpa/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jpa/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jpa.jar:lib/persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jpa.jar;lib\persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
This command enhances all class files specified in the persistence-unit "Tutorial". If you accidentally
omitted this step, at the point of running your application and trying to persist an object, you would
get a ClassNotPersistableException thrown. The use of the enhancer is documented in more detail in
the Enhancer Guide. The output of this step are a set of class files that represent persistable classes.
So we created an EntityManagerFactory for our "persistence-unit" called "Tutorial". Now that the
application has an EntityManager it can persist objects. This is performed as follows
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Please note that the finally step is important in that it tidies up connections to the datastore and the
EntityManager.
Now we want to retrieve some objects from persistent storage, so we will use a "Query". In our case
we want access to all Product objects that have a price below 150.00 and ordering them in ascending
order.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JPA book will provide many examples.
Using Ant (you need the included persistence.xml to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/persistence-api.jar:lib/datanucleus-core.jar:lib/datanucleus-mongodb.jar:
lib/datanucleus-api-jpa.jar:lib/mongo-java.jar:target/classes/:.
org.datanucleus.samples.jpa.tutorial.Main
Manually on Windows :
java -cp lib\persistence-api.jar;lib\datanucleus-core.jar;lib\datanucleus-mongodb.jar;
lib\datanucleus-api-jpa.jar;lib\mongo-java.jar;target\classes\;.
org.datanucleus.samples.jpa.tutorial.Main
Output :
End of Tutorial
In the above simple tutorial we showed how to employ JPA and persist objects to a MongoDB
database. Obviously this just scratches the surface of what you can do, and to use JPA requires
minimal work from the user. In this second part we show some further things that you are likely to
want to do.
This file should be placed at the root of the CLASSPATH under META-INF.
184.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the DataNucleus SchemaTool to generate the tables where these domain
objects will be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked
from Maven/Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to
update the src/java/META-INF/persistence.xml file with your database details. Here we have a sample
file (for HSQLDB) that contains
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/persistence-api.jar:lib/datanucleus-core.jar:
lib/datanucleus-mongodb.jar:lib/datanucleus-api-jpa.jar:lib/{mongo-java.jar}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\persistence-api.jar;lib\datanucleus-core.jar;
lib\datanucleus-mongodb.jar;lib\datanucleus-api-jpa.jar;lib\{mongo-java.jar}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
This will generate the required tables, indexes, and foreign keys for the classes defined in the
annotations and orm.xml Meta-Data file.
185.1.1 Background
An application can be JPA-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the Dali Eclipse plugin coupled with the DataNucleus Eclipse
plugin. Alternatively the project could use Ant, Maven2 or some other build tool. In this case this
tutorial should be used as a guiding way for using DataNucleus in the application. The JPA process is
quite straightforward.
185.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Inventory
{
@Id
String name = null;
package org.datanucleus.samples.jpa.tutorial;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Product
{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
long id;
...
}
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @Entity and their primary key field(s)
with @Id In addition we defined a valueStrategy for Product field id so that it will have its values
generated automatically. In this tutorial we are using application identity which means that all objects
of these classes will have their identity defined by the primary key field(s). You can read more in
application identity when designing your systems persistence.
src/main/java/org/datanucleus/samples/jpa/tutorial/Book.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jpa/tutorial/Book.class
target/classes/org/datanucleus/samples/jpa/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jpa/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jpa.jar:lib/persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jpa.jar;lib\persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
This command enhances all class files specified in the persistence-unit "Tutorial". If you accidentally
omitted this step, at the point of running your application and trying to persist an object, you would
get a ClassNotPersistableException thrown. The use of the enhancer is documented in more detail in
the Enhancer Guide. The output of this step are a set of class files that represent persistable classes.
So we created an EntityManagerFactory for our "persistence-unit" called "Tutorial". Now that the
application has an EntityManager it can persist objects. This is performed as follows
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Please note that the finally step is important in that it tidies up connections to the datastore and the
EntityManager.
Now we want to retrieve some objects from persistent storage, so we will use a "Query". In our case
we want access to all Product objects that have a price below 150.00 and ordering them in ascending
order.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JPA book will provide many examples.
Using Ant (you need the included persistence.xml to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/persistence-api.jar:lib/datanucleus-core.jar:lib/datanucleus-hbase.jar:
lib/datanucleus-api-jpa.jar:lib/{hbase_jars}:target/classes/:.
org.datanucleus.samples.jpa.tutorial.Main
Manually on Windows :
java -cp lib\persistence-api.jar;lib\datanucleus-core.jar;lib\datanucleus-hbase.jar;
lib\datanucleus-api-jpa.jar;lib\{hbase_jars};target\classes\;.
org.datanucleus.samples.jpa.tutorial.Main
Output :
End of Tutorial
In the above simple tutorial we showed how to employ JPA and persist objects to a HBase database.
Obviously this just scratches the surface of what you can do, and to use JPA requires minimal work
from the user. In this second part we show some further things that you are likely to want to do.
This file should be placed at the root of the CLASSPATH under META-INF.
185.2.2 Step 7 : Generate any schema required for your domain classes
This step is optional, depending on whether you have an existing database schema. If you haven't,
at this point you can use the DataNucleus SchemaTool to generate the tables where these domain
objects will be persisted. DataNucleus SchemaTool is a command line utility (it can be invoked
from Maven/Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to
update the src/java/META-INF/persistence.xml file with your database details. Here we have a sample
file (for HSQLDB) that contains
</persistence>
Now we need to run DataNucleus SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/persistence-api.jar:lib/datanucleus-core.jar:
lib/datanucleus-hbase.jar:lib/datanucleus-api-jpa.jar:lib/{hbase-jars}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\persistence-api.jar;lib\datanucleus-core.jar;
lib\datanucleus-hbase.jar;lib\datanucleus-api-jpa.jar;lib\{hbase-jars}
org.datanucleus.store.schema.SchemaTool
-create -api JPA -pu Tutorial
This will generate the required tables, indexes, and foreign keys for the classes defined in the
annotations and orm.xml Meta-Data file.
186.1.1 Background
An application can be JPA-enabled via many routes depending on the development process of the
project in question. For example the project could use Eclipse as the IDE for developing classes. In
that case the project would typically use the Dali Eclipse plugin coupled with the DataNucleus Eclipse
plugin. Alternatively the project could use Ant, Maven2 or some other build tool. In this case this
tutorial should be used as a guiding way for using DataNucleus in the application. The JPA process is
quite straightforward.
186.1.3 Step 1 : Take your model classes and mark which are persistable
For our tutorial, say we have the following classes representing a store of products for sale.
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
package org.datanucleus.samples.jpa.tutorial;
So we have a relationship (Inventory having a set of Products), and inheritance (Product-Book). Now
we need to be able to persist objects of all of these types, so we need to define persistence for them.
There are many things that you can define when deciding how to persist objects of a type but the
essential parts are
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Inventory
{
@Id
String name = null;
package org.datanucleus.samples.jpa.tutorial;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Product
{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
long id;
...
}
package org.datanucleus.samples.jpa.tutorial;
@Entity
public class Book extends Product
{
...
}
Note that we mark each class that can be persisted with @Entity and their primary key field(s)
with @Id In addition we defined a valueStrategy for Product field id so that it will have its values
generated automatically. In this tutorial we are using application identity which means that all objects
of these classes will have their identity defined by the primary key field(s). You can read more in
application identity when designing your systems persistence.
src/main/java/org/datanucleus/samples/jpa/tutorial/Book.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Inventory.java
src/main/java/org/datanucleus/samples/jpa/tutorial/Product.java
src/main/resources/META-INF/persistence.xml
target/classes/org/datanucleus/samples/jpa/tutorial/Book.class
target/classes/org/datanucleus/samples/jpa/tutorial/Inventory.class
target/classes/org/datanucleus/samples/jpa/tutorial/Product.class
The first thing to do is compile your domain/model classes. You can do this in any way you wish, but
the downloadable JAR provides an Ant task, and a Maven2 project to do this for you.
Using Ant :
ant compile
Using Maven :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something like
this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-api-jpa.jar:lib/persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-api-jpa.jar;lib\persistence-api.jar
org.datanucleus.enhancer.DataNucleusEnhancer
-api JPA -pu Tutorial
This command enhances all class files specified in the persistence-unit "Tutorial". If you accidentally
omitted this step, at the point of running your application and trying to persist an object, you would
get a ClassNotPersistableException thrown. The use of the enhancer is documented in more detail in
the Enhancer Guide. The output of this step are a set of class files that represent persistable classes.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Please note that the finally step is important in that it tidies up connections to the datastore and the
EntityManager.
Now we want to retrieve some objects from persistent storage, so we will use a "Query". In our case
we want access to all Product objects that have a price below 150.00 and ordering them in ascending
order.
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = em.getTransaction();
try
{
tx.begin();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}
Clearly you can perform a large range of operations on objects. We can't hope to show all of these
here. Any good JPA book will provide many examples.
Using Ant (you need the included persistence.xml to specify your database)
ant run
Using Maven:
mvn exec:java
Manually on Linux/Unix :
java -cp lib/persistence-api.jar:lib/datanucleus-core.jar:lib/datanucleus-neo4j.jar:
lib/datanucleus-api-jpa.jar:lib/{neo4j-jars}:target/classes/:.
org.datanucleus.samples.jpa.tutorial.Main
Manually on Windows :
java -cp lib\persistence-api.jar;lib\datanucleus-core.jar;lib\datanucleus-neo4j.jar;
lib\datanucleus-api-jpa.jar;lib\{neo4j_jars};target\classes\;.
org.datanucleus.samples.jpa.tutorial.Main
Output :
End of Tutorial
In the above simple tutorial we showed how to employ JPA and persist objects to a Neo4J database.
Obviously this just scratches the surface of what you can do, and to use JPA requires minimal work
from the user. In this second part we show some further things that you are likely to want to do.
This file should be placed at the root of the CLASSPATH under META-INF.
187.1.1 Requirements
For using the IDE, you must install Eclipse 3.2, Eclipse Dali and the DataNucleus Eclipse plug-in.
For using the DataNucleus runtime, see JPA annotations.
187.1.2 Demo
package org.jpox.demo;
import java.io.Serializable;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Account implements Serializable {
@Id
@Column(name="ACCOUNT_ID")
private BigDecimal accountId;
public Account() {
super();
}
package org.jpox.demo;
import java.math.BigDecimal;
import java.util.Random;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
javax.jdo.PersistenceManagerFactoryClass=org.jpox.PersistenceManagerFactoryImpl
javax.jdo.option.ConnectionDriverName=oracle.jdbc.driver.OracleDriver
javax.jdo.option.ConnectionURL=jdbc:oracle:thin:@127.0.0.1:1521:XE
javax.jdo.option.ConnectionUserName=test
javax.jdo.option.ConnectionPassword=password
org.jpox.autoCreateSchema=true
org.jpox.metadata.validate=false
org.jpox.autoStartMechanism=XML
org.jpox.autoCreateTables=true
org.jpox.validateTables=false
org.jpox.autoCreateColumns=true
org.jpox.autoCreateConstraints=true
org.jpox.validateConstraints=false
org.jpox.autoCreateSchema=true
org.jpox.rdbms.stringDefaultLength=255
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="movie-unit">
<provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
<jta-data-source>movieDatabase</jta-data-source>
<non-jta-data-source>movieDatabaseUnmanaged</non-jta-data-source>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/
</properties>
</persistence-unit>
</persistence>
Note that you may have to set the persistence property datanucleus.jtaLocator and
datanucleus.jtaJndiLocation to find your JNDI data sources.
javax.persistence.provider
javax.persistence.transactionType
javax.persistence.jtaDataSource
javax.persistence.nonJtaDataSource
So, for example, DataNucleus can become the default provider via setting
CATALINA_OPTS=-Djavax.persistence.provider=org.datanucleus.api.jpa.PersistenceProviderImpl
You must of course add the DataNucleus libraries to <tomee-home>/lib/ for this to work.
Add:
<tomee-home>/lib/datanucleus-core-4.0.0-m4.jar
<tomee-home>/lib/datanucleus-api-jpa-4.0.0-m4.jar
<tomee-home>/lib/datanucleus-rdbms-4.0.0-m4.jar
Remove (optional):
<tomee-home>/lib/asm-3.2.jar
<tomee-home>/lib/commons-lang-2.6.jar
<tomee-home>/lib/openjpa-2.2.0.jar
<tomee-home>/lib/serp-1.13.1.jar
The configuration of the REST API consists in the deployment of jar libraries to the CLASSPATH
and the configuration of the servlet in the /WEB-INF/web.xml. After it's configured, all persistent
classes are automatically exposed via RESTful HTTP interface. You need to have enhanced versions
of the model classes in the CLASSPATH.
189.2.1 Libraries
DataNucleus REST API requires the libraries: datanucleus-core, datanucleus-api-rest, datanucleus-
api-jdo, jdo-api, as well as datanucleus-rdbms (or whichever datastore you wish to persist to if not
RDBMS). You would also require JPA API jar if using JPA metadata (XML/annotations) in your
model classes. In WAR files, these libraries are deployed under the folder /WEB-INF/lib/.
189.2.2 web.xml
The DataNucleus REST Servlet class implementation is org.datanucleus.api.rest.RestServlet. It has to
be configured in the /WEB-INF/web.xml file, and it takes one initialisation parameter.
Parameter Description
persistence-context Name of a PMF (if using jdoconfig.xml), or the name of
a persistence-unit (if using persistence.xml) accessible
to the servlet
<servlet>
<servlet-name>DataNucleus</servlet-name>
<servlet-class>org.datanucleus.api.rest.RestServlet</servlet-class>
<init-param>
<param-name>persistence-context</param-name>
<param-value>myPMFName</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DataNucleus</servlet-name>
<url-pattern>/dn/*</url-pattern>
</servlet-mapping>
...
</web-app>
changing myPMFName to the name of your PMF, or the name of your persistence-unit, and changing
/dn/* to the URL pattern where you want DataNucleus REST API calls to be answered.
The persistence to the datastore in your application is performed via HTTP methods as following:
POST http://localhost/dn/mydomain.Greeting
{"author":null,
"content":"test insert",
"date":1239213923232}
Response:
{"author":null,
"content":"test insert",
"date":1239213923232,
"id":1}
POST http://localhost/dn/mydomain.Person
{"firstName":"Joe",
"lastName":"User",
"age":15}
Response:
{"firstName":"Joe",
"lastName":"User",
"age":15,
"_id":2}
PUT http://localhost/dn/mydomain.Greeting/1
{"content":"test update"}
PUT http://localhost/dn/mydomain.Person/2
{"age":23}
GET http://localhost/dn/mydomain.Greeting
Response:
[{"author":null,
"content":"test",
"date":1239213624216,
"id":1},
{"author":null,
"content":"test2",
"date":1239213632286,
"id":2}]
GET http://localhost/dn/mydomain.Person/2
Response:
{"firstName":"Joe",
"lastName":"User",
"age":23,
"_id":2}
Note that it replies with a JSONObject that has "_id" property representing the datastore id.
GET http://localhost/dn/mydomain.Greeting?content=='test'
Response:
[{"author":null,
"content":"test",
"date":1239213624216,
"id":1}]
GET http://localhost/dn/google.maps.Markers/{"class":"com.google.appengine.api.datastore.Key","id":1001,"
Response:
{"class":"google.maps.Markers",
"key":{"class":"com.google.appengine.api.datastore.Key",
"id":1001,
"kind":"Markers"
},
"markers":[
{"class":"google.maps.Marker",
"html":"Paris",
"key":{"class":"com.google.appengine.api.datastore.Key",
"id":1,
"kind":"Marker",
"parent":{"class":"com.google.appengine.api.datastore.Key",
"id":1001,
"kind":"Markers"
}
},
"lat":48.862222,
"lng":2.351111
}
]
}