23 Using TopLink with NoSQL Databases
This chapter includes the following sections:
Use Case
Users need to use TopLink with NoSQL data sources.
Solution
TopLink provides support for multiple NoSQL data sources. This solution illustrates using Oracle NoSQL and MongoDB.
Components
-
TopLink 12c (12.1.2.0.0) or later.
Note:
TopLink's core functionality is provided by EclipseLink, the open source persistence framework from the Eclipse Foundation. EclipseLink implements Java Persistence API (JPA), Java Architecture for XML Binding (JAXB), and other standards-based persistence technologies, plus extensions to those standards. TopLink includes all of EclipseLink, plus additional functionality from Oracle.
-
EclipseLink 2.4 or later
-
NoSQL datasource.
-
JCA Adapter.
Sample
See the following EclipseLink samples for related information:
Introduction to the Solution
EclipseLink supports access to NoSQL data through the JavaEE Connector Architecture. You must use a JCA adapter (provided by EclipseLink, a third party, or custom built).
Most NoSQL data is hierarchical in form so using embeddable objects is common. Some NoSQL adaptors support XML data, so NoSQL mapped objects can use XML mappings when mapping to XML.
Implementing the Solution
This section contains the following tasks for converting objects to and from JSON documents.
Task 2: Mapping the Data
You can configure mappings to NoSQL data with the EclipseLink @NoSQL
annotation and <no-sql>
XML element. The @NoSQL
annotation defines the class as mapping to non-relational data. You can use @NoSQL
with JPA Entity or Embeddable classes.
The @NoSQL
annotation allows you to specify the dataType
and dataFormat
of the data. The dataType
will vary, depending on your NoSQL datasource:
-
For MongoDB,
dataType
is the collection name that the JSON documents are stored to. -
For Oracle NoSQL,
dataType
is the first part of the major key value.
The dataFormat
depends on the type structure (data format) of data being stored.
-
For MongoDB, use
MAPPED
for its structured database. -
For Oracle NoSQL, use
MAPPED
(for key/value data) orXML
(for a single XML document).
Example 23-1 illustrates how to use @NoSQL
with @Entity
and @Embeddable
classes.
Example 23-1 Using @NoSql Annotation with JSON
@Entity @NoSQL(dataType="orders", dataFormat=DataFormatType.MAPPED) public class Order { @Id @GeneratedValue @Field(name="_id") private long id; @Basic @Field(name="description") private String description; @Embedded @Field(name="deliveryAddress") private Address deliveryAddress @ElementCollection @Field(name="orderLines") private List<OrderLine> orderLines; @ManyToOne @JoinField(name="customerId") private Customer customer; } @Embeddable @NoSQL(dataFormat=DataFormatType.MAPPED) public class OrderLine { @Field(name="lineNumber") private int lineNumber; @Field(name="itemName") private String itemName; @Field(name="quantity") private int quantity; }
Task 3: Defining IDs
With EclipseLink, you can use any field (or set of fields) as your ID when using a non-relational database, just like any other relational Entity. You can use a natural ID (that is, assigned by the application) or a generated ID (that is, assigned by EclipseLink).
MongoDB also requires an _id
field in every document. If no _id
field is present, Mongo will automatically generate and assign the _id
field using an OID (object identifier), which is similar to a UUID (universally unique identifier).
-
To use a natural ID as the Mongo ID, simply name the field as
_id
by using the@Field
(or@Column
) annotation without any of the relational details.For example:
@Field(name="_id") private long id;
-
To use the generated Mongo OID as your ID, simply include
@Id
,@GeneratedValue
, and@Field(name="_id")
annotations in the object's ID field mapping.The
@GeneratedValue
tells EclipseLink to use the Mongo OID to generate this ID value. To use a UUID instead of the Mongo OID, use the @UUIDGenerator annotation.Note:
MongoDB does not support
@SequenceGenerator
or@TableGenerator
nor theIDENTITY
,TABLE
, andSEQUENCE
generation types.The ID of the Mongo OID or UUID is not a numerical value; you must map it as a
String
orbyte[]
.For example:
@Id @GeneratedValue @Field(name="_id") private String id;
Task 4: Defining Mappings
With non-relational databases, EclipseLink maps objects to structured data such as XML or JSON. NoSQL supports all existing JPA mapping annotations and XML, including embedded data and embedded collections. If you do not define a mapping annotation (or XML) for an attribute EclipseLink uses the default mapping.
Basic Mappings
Because the NoSQL defaults follow the JPA defaults, most simple mappings do not require any configuration. Field names used in the Mongo BSON document will mirror the object attribute names (in uppercase). To use a different BSON field name, use the @Field
annotation.
Note:
Do not use @Column
or @JoinColumn
. Instead use @Field
and @JoinField
. Additionally, the @JoinTable
and @CollectionTable
annotations are not supported or required.
Embedded Values
Use the @Embedded
annotation to persist embedded values and the @ElementCollection
annotation for embedded collections. Because all data is stored in the XML document, no separate table (that is, @CollectionTable
) is needed. Additionally, because embedded objects are nested in the document and do not require unique field names, the @AttributeOverride
attribute is not needed.
Note:
Normally, you will not need to use the @Embedded
annotation, since it will default correctly.
However, EclipseLink does not default @ElementCollection
mappings, therefore you must include that annotation.
Relationships
You should use the relationship annotations (such as @OneToOne
, @ManyToOne
, @OneToMany
and @ManyToMany
) only with external relationships. Relationships within the document should use the Embedded Values.
EclipseLink fully supports external relationships to other documents by using a foreign key. The ID of the target object is stored in the source object's document. For a collection, a collection of IDs is stored. Use the @JoinField
annotation to define the name of the foreign key field in the BSON document.
Note:
EclipseLink does not support the mappedBy
option for relationships with non-relational databases, as the foreign keys would need to be stored on both sides.
You can also define a relationship mapping by using a query. However you must use a DescriptorCustomizer
instead of an annotation.
Example 23-2 Sample Mappings
@Basic private String description; @Basic private double totalCost = 0; @Embedded private Address billingAddress; @Embedded private Address shippingAddress; @ElementCollection private List<OrderLine> orderLines = new ArrayList<OrderLine>(); @ManyToOne(fetch=FetchType.LAZY) private Customer customer;
Task 5: Using Locking
Locking support is dependent on the NoSQL platform. Some NoSQL platforms may offer support for optimistic version locking.
-
Oracle NoSQL – Locking is not supported.
-
MongoDB – Version locking is supported.
Note:
MongoDB does not support transactions. If a lock error occurs during a transaction, any objects that have been previously written will not be rolled back.
If the NoSQL platform does not support locking, you can use the @Version
annotation (as shown in Example 23-3) to validate objects on merge()
operations.
Example 23-3 Using @Version
@Version private long version; ...
Task 6: Defining Queries
Querying in NoSQL is dependent on the NoSQL platform. Some NoSQL data-sources may support dynamic querying through their own query language, others may not support querying at all.
JPQL Queries
The Java Persistence Query Language (JPQL) is the query language defined by JPA. JPQL can be used for reading (SELECT
), as well as bulk updates (UPDATE
) and deletes (DELETE
). You can use JPQL in a NamedQuery
(through annotations or XML) or in dynamic queries using the EntityManager createQuery()
API.
-
Oracle NoSQL – Supports
find()
and JPQL and Criteria by Id or with no WHERE clause. -
MongoDB – Supports JPQL and Criteria queries, with some restrictions: joins, sub-selects, group by and certain database functions are not supported.
Example 23-4 Oracle NoSQL JPQL Examples
Example 23-5 MongoDB JPQL Examples
Query query = em.createQuery("Select o from Order o where o.totalCost > 1000"); List<Order> orders = query.getResultList();
Query query = em.createQuery("Select o from Order o where o.description like 'Pinball%'"); List<Order> orders = query.getResultList();
Query query = em.createQuery("Select o from Order o join o.orderLines l where l.description = :desc"); query.setParameter("desc", "shipping"); List<Order> orders = query.getResultList();
Query query = em.createQuery("Select o.totalCost from Order o"); List<BigDecimal> orders = query.getResultList();
Native Queries
Native SQL queries are not translated, and passed directly to the database. SQL queries can be used for advanced queries that require database specific syntax.
Although native SQL queries are not supported with NoSQL, some NoSQL platforms have their own, native query language. EclipseLink supports JPA native queries using that language.
-
MongoDB – Supports JPA native queries by using the MongoDB native command language.
Example 23-6 Oracle NoSQL Native Query
Example 23-7 MongoDB Native Query
Query query = em.createNativeQuery("db.ORDER.findOne({\"_id\":\"" + oid + "\"})", Order.class); Order order = (Order)query.getSingleResult();
Task 7: Connecting to the Database
EclipseLink connects to NoSQL databases through the persistence.xml file. Use the <eclipselink.target-database>
property to define the specific NoSQL platform. You must also define a connection with the <eclipselink.nosql.connection-spec>
property. Additional connection values (such as the db
, port
, and host
can also be defined.
Note:
To connect to a cluster of Mongo databases, enter a comma, separated list of values for the host
and port
.
Example 23-8 Oracle NoSQL persistence.xml Example
Example 23-9 MongoDB persistence.xml Example
<persistence 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 persistence_2_0.xsd" version="2.0">
<persistence-unit name="acme" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.target-database" value="org.eclipse.persistence.nosql.adapters.mongo.MongoPlatform"/>
<property name="eclipselink.nosql.connection-spec" value="org.eclipse.persistence.nosql.adapters.mongo.MongoConnectionSpec"/>
<property name="eclipselink.nosql.property.mongo.port" value="27017, 27017"/>
<property name="eclipselink.nosql.property.mongo.host" value="host1, host2"/>
<property name="eclipselink.nosql.property.mongo.db" value="acme"/>
</properties>
</persistence-unit>
</persistence>
Additional Resources
See the following resources for more information about the technologies and tools used to implement the solutions in this chapter:
-
Developing Persistence Architectures Using Oracle Toplink Document Data Bindings
-
Java Persistence API (JPA) Extensions Reference for Oracle TopLink
-
EclipseLink Extensions Incubator:
http://wiki.eclipse.org/EclipseLink/Development/Incubator/Platform