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
- Allowing Null
- Default Values
- Unique Fields
- Enum Fields
- Item Fields
- Further Reading
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.
-
The default creation constructor does not
include a parameter for
loginCounter
anymore. This is because a value forloginCounter
is not needed for constructing an instance of Customer anymore. If you still want to haveloginCounter
included into the default creation constructor add a@WrapperInitial
annotation to the field:@WrapperInitial static final LongField loginCounter = new LongField().optional();
-
The signature of the getter and setter for
loginCounter
changed. Before, it used the primitive typelong
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:
- Transactions Trail tells you how to use transactions.
- Searching Trail gives you an introduction into the searching capabilities of exedio persistence.
- Field Reloaded Trail covers all the more specific possibilities to store data with exedio persistence.
- Web Application Trail shows you the little differences when using the framework within a web container.