View on GitHub

exedio persistence

Persistence Framework for Java.

Go to top

Field Trail

While the Introduction Trail covered string fields only, this trails visits all kinds of other fields as well as the more special features of string fields.

Contents

Common Primitives

exedio persistence provides fields for all commonly used java primitives as well as for String and java.util.Date. This looks like this.

class Customer extends Item
{
   static final StringField email =
       new StringField();
   static final DateField registrationDate =
       new DateField();
   static final LongField loginCounter =
       new LongField();
   static final IntegerField subsequentLoginFailures =
       new IntegerField();
   static final DoubleField priceCoefficient =
       new DoubleField();
}

Run the instrumentor, and you get setters/getters for all the new fields. Also the default creation constructor will have parameters for the initial values of these fields.

Allowing Null

You may have noticed, that you cannot store null into any of the fields. This is because fields are mandatory by default. You can easily change this to optional.

static final LongField loginCounter =
    new LongField().optional();

When you run the instrumentor, you may notice a few changes in the generated code.

  1. The default creation constructor does not include a parameter for loginCounter anymore. This is because a value for loginCounter is not needed for constructing an instance of Customer anymore. If you still want to have loginCounter included into the default creation constructor add a @cope.initial tag to the field:
    /**
     * @cope.initial
     */
    static final LongField loginCounter =
        new LongField().optional();
    
  2. The signature of the getter and setter for loginCounter changed. Before, it used the primitive type long
    long getLoginCounter() { ... }
    void setLoginCounter(long loginCounter) { ... }
    
    but now being optional it uses the wrapper class Long.
    Long getLoginCounter() { ... }
    void setLoginCounter(Long loginCounter) { ... }
    

Default Values

What we did above with the login counter is probably not, what one would want in a real project. One would probably rather want to make it mandatory and initialize it to zero for any newly created customer. This can be specified by:

static final LongField loginCounter =
    new LongField().defaultTo(0l);

In the instrumented code the type for setter/getter changes back to primitives again, but the default creation constructor does also not have a parameter for loginCounter.

If you want to make the current date the default value of any date field, then of course defaultTo(new Date()) won't work. Use instead:

static final DateField registrationDate =
    new DateField().defaultToNow();

Unique Fields

Probably you wouldn't want to have more than one customer for any given email address. Peace of cake:

static final StringField email =
    new StringField().unique();

If you violate this unique constraint either via a creation constructor or via setEmail this will throw a UniqueViolationException.

In the instrumented code you will find a new convenience method

static final Customer findByEmail(String email);

which does, what it's name suggests.

For unique constraints across multiple fields the notation is slightly different:

class MatrixElement extends Item
{
   static final IntegerField x =
       new IntegerField();
   static final IntegerField y =
       new IntegerField();
   static final UniqueConstraint xy =
       new UniqueConstraint(x, y);
}

Enum Fields

A common pattern in persistent models are fields, the can have a value out of a predefined set of values. For that purpose java provides enums since JDK 1.5. exedio persistence allows you to store such enums in fields just like any other primitive:

class Customer extends Item
{
   static enum Status
   {
      STANDARD,
      GOLD,
      PLATINUM,
   }

   static final EnumField<Status> status =
       newEnumField(Status.class);
}

Item Fields

An item fields stores a reference to another persistent item. It's the object oriented counter part of a foreign key constraint in relational databases.

Here an example of a product referencing the product group it belongs to:

class ProductGroup extends Item
{
   static final StringField name =
       new StringField();
}
class Product extends Item
{
   static final ItemField<ProductGroup> productGroup =
       newItemField(ProductGroup.class);
}

As specified above, a product group cannot be deleted, if there are still products referencing this product group. This is because the default delete policy of an item field is forbid. Alternativly you may want to have all products of a product group to be deleted, if that product group is deleted. To achive this, change the delete policy to cascade.

static final ItemField<ProductGroup> productGroup =
    newItemField(ProductGroup.class, CASCADE);

Yet another alternative is to set the item field to null on all products referencing the product group to be deleted. This is done by:

static final ItemField<ProductGroup> productGroup =
    newItemField(ProductGroup.class, NULLIFY);

Of course nullify makes no sense for a mandatory field. Therefore such item fields are optional by default.

Further Reading

This was the field trail of the tour. You may now proceed to trails: