JDO : Constraints

A datastore often provides ways of constraining the storage of data to maintain relationships and improve performance. These are known as constraints and they come in various forms. These are :-

  • 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.

Indexes

Applicable to RDBMS, NeoDatis, MongoDB.

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

public class Booking
{
    private int bookingType;
    ...
}

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

        <index name="BOOKING_TYPE_INDEX" unique="true"/>

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 :-


Unique constraints

Applicable to RDBMS, NeoDatis, MongoDB.

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 :-


Foreign Keys

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

public class Hotel
{
    private Set rooms;
    ...
}

public class Room
{
    private int numberOfBeds;
    ...
}

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 :-


Primary Keys

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>

This creates a PK constraint with name "HOTEL_ROOM_PK".

See also :-