Resource model declaration
Model declaration overview
Presently, each model maps to a single database table and the following list provides other basic information:
- Each resource model is declared in its own JSON definition file in
{app_root}/app/models. The file name defines the resource name, which in turn defines the default table name. You can also customize the table name. - A resource model MUST have a Fields declaration. Each field represents a database field.
- A resource model defines the data shape of a default collection. Each declared resource model automatically gets a default collection on which to perform LCRUD operations.
- Named collections are custom collections that can be optionally declared in the model declaration.
- ZRM provides database generation functionality, as well as programmatic and HTTP REST access APIs.
A resource model example
The file name defines the resource name, which in turn defines the default table name.
If the following definition were in the app/models/persons.json file, it would
define the the persons resource model that as having first_name and
birth_date fields:
{
"fields" : {
"first_name": {"type": "string"},
"birth_date": {"type": "date"}
}
}
The first_name and birth_date fields are fields of the resource model. Each
field is assigned metadata that modify its behavior at runtime (for example,
validation rules) and field attribute maps to a database column.
The previous persons model would create a database table as shown in the following example:
CREATE TABLE persons (
first_name VARCHAR(30) NOT NULL,
birth_date DATE,
id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
START WITH 100, INCREMENT BY 1) NOT NULL,
updated TIMESTAMP NOT NULL
)
Technical notes:
- The name of the table,
persons, is automatically derived from file name{app_root}/app/models/persons.jsonbut can be overridden. See the following table customization section. - An
idfield is added automatically. Keys are generated by default and begin at 100, incrementing by 1. - An
updatedfield is added automatically. - The CREATE TABLE SQL in this example is formatted using Derby syntax.
The generated syntax will be selected based on the configured database in
zero.configand must be among the supported databases.
Fields
The most important part of a resource model (and the only required part) is the list of fields it declares. Fields are specified by a data structure assigned to fields as shown in the previous example.
Field name restrictions
Several restrictions exist on resource model field names. A field name:
- cannot be a Java™, Groovy, or SQL reserved word
- cannot contain a hyphen
- cannot begin with an underscore or contain more than one underscore in a row, due to the way the query lookup syntax works
- cannot be an empty string
- must begin with an alpha character
- cannot contain spaces
Field types
Each field in your resource model must have a specified type string. This string maps to a field class. The resource model uses the field class types to determine the following things:
- The database column type (for example,
INTEGER,VARCHAR). - The minimal validation requirements.
The supported data types for a resource declaration are:
string
Field for small- to large-sized strings and other character-based values. The
string field can optionally take a format to modify it's validating
behavior.
| Parameter | Required | Description |
|---|---|---|
| max_length | No | The maximum character length is by default 50.
However, string can take max_length as extra
parameter which specifies the maximum character
length of the field. The max_length is enforced at
the database level and in the Resource Model
validation. |
| format | No | The format parameter modifies the validating
behavior of string types. Valid values are:
large, email, phone, region, and
any. |
The large format optimizes for very large amounts of character-based values.
The email and phone formats use a regular expression during validation.
Fields with phone specified for the format must validate against
(999) 999-9999 style of phone numbers. The region format is a 2
character abbreviation for a U.S. state. The any format stores large byte
arrays. Over HTTP, these values are transferred using BASE64 encoding. Thus,
it is a format of string.
boolean
A true/false field. Valid values for true are: true, 't', 'true', 'True', '1',
or 1. Valid values for false are false, 'f', 'false', 'False', '0', 0.
date
A field representing a date (day, month, and year). Valid values take the form
of yyyy-MM-dd.
| Parameter | Required | Description |
|---|---|---|
| auto_create | No |
false is default. If set to true, when the
member resource is first created ZRM will
automatically set the value of this field to the
current date. This parameter is mutually exclusive
with auto_update. |
| auto_update | No |
false is default. If set to true, when the
member resource is first created and each
subsequent update ZRM will automatically set the
value of this field to the current date. This
parameter is mutually exclusive with auto_update. |
date-time
A field representing an instance or timestamp (second, minute, hour, day, month,
and year). Valid values take the form of any of yyyy-MM-dd,
yyyy-MM-dd HH:mm, or yyyy-MM-dd HH:mm:ss.
time
A field representing a time independent of a date (second, minute, and hour).
Valid values take the form of HH:mm and HH:mm:ss.
decimal
A decimal number represented in Java™ by a java.math.BigDecimal.
| Parameter | Required | Description |
|---|---|---|
| max_digits | Yes | The maximum number of digits allowed in the number. |
| decimal_places | Yes | The number of decimal places to store with the number (the decimal number's scale). |
float
A floating-point number represented in Java™ by a float.
integer
An integer.
Using fields
A resource declaration using the types described in the previous sections looks like the following example:
{
"fields" : {
"name": {"type": "string", "max_length":50},
"birth_date": {"type": "date"},
"age": {"type": "integer"},
"salary": {"type": "decimal", "max_digits": 9, "decimal_places": 2},
"takes_lunch_at": {"type": "time"},
"favorite_moment": {"type": "date-time"},
"favorite_number": {"type": "float"},
"biography": {"type": "string", "format": "large"},
"address ":{"type":"string", "max_length": 60},
"city": {"type": "string", "max_length": 45},
"state": {"type": "string", "format":"region"},
"phone": {"type": "string", "format":"phone"},
"email": {"type":"string", "format":"email"}
}
}
Field options
The following optional arguments are available to all field types:
required
Default is false, which means all fields by default are not required to have
a value. If true, the field is required. This constraint is enforced during:
-
create()andupdate()on a Collection -
validate()on a Member instance - POST and PUT on the HTTP resource
null.
Example:
{
"fields" : {
"first_name": {"type": "string", "max_length": 25},
"last_name": {"type": "string", "max_length": 35, "required": true},
"email": {"type": "string", "format": "email", "required": false}
}
}
In this example, first_name and email are not required whereas
last_name is required.
label
The label parameter is a "friendly" name for the field.
Example:
"first_name": {"type":"string", "label": "Your first name"}
description
The description parameter allows the developer to provide detailed
information about the field. Not only is this useful for documentation purposes,
but it can also be used in user interfaces to provide additional help to assist
application users.
Example:
"first_name": {"type":"string", "description": "The first name is your given name"}
default_value
When the default_value parameter is set it is used when the create() method
is called and no value has been set for the field. It is not used when a resource
instance is updated.
Example:
"first_name": {"type":"string", "default_value": "Bob"}
default_value can
options
A list of values that are valid for the field.
Named collections
Resources are accessed using collection-like constructs. A filter can be applied to such collection to create a new collection that is a subset of the original. A filter condition can be applied either programmatically or using HTTP at runtime. Filters can also be specified in the model declaration by defining a named collection:
{
"fields" : {
"first_name": {"type": "string", "max_length": 30},
"birth_date": {"type": "date"},
"is_child": {"type": "boolean"}
},
"collections" : {
"children": {"memberFilters": {"is_child": true}},
"christmas_birthdays": {"memberFilters": {"birth_date__day": "25", "birth_date__month": "12"}}
}
}
Database generation
ZRM can generate a database table for each resource using a top down mapping scheme. By default, the name of the model file maps to the name of the database table and the field names map to column names. Each table also has a special field, id, that is the unique identifier for that resource.
Synchronizing the database
In order to create required database artifacts, you must first run the
model sync command from the command line. This command will read the model
declarations, determine what required database tables are missing and create
them for you. However, if the table exists, then ZRM leaves it intact. If you
modify a model file and want the table be recreated, you must run
model reset. This command drops the database and calls model sync.
Following is an example of executing the model sync command from the command
line:
zero model sync
firstname and then later, after data has been saved into the table, you
change the field name to fname, there is no way to determine that
firstname is fname. Automation would drop the firstname column
(along with all its data) and a fname column is created.
Because of this limitation, any changes you make to your ZRM declaration will
need to manually changed. To do this, you can use the runsql command line
command from the zero.data module. The following example illustrates this:
zero runsql alter_table.sql
The zero.data documentation has more information on the runsql command.
Loading initial data fixtures
In addition to creating the database tables at application initialization, ZRM
searches for initial_data.json files across all dependencies in
/app/models/fixtures directories and loads its data into the database.
Once the table has been initialized with the data in initial_data.json the data is not loaded until the table is manually dropped.
The following initial_data.json example assumes the following model
declaration at /app/models/persons.json:
{
"fields": {
"first_name":{"type": "string"},
"birth_date":{"type": "date"},
"is_child":{"type": "boolean"}
}
}
The following example of an initial_data.json file would load two instances
of the above model into the backing database table:
[
{
"type": "persons",
"fields": {
"id" : "1",
"first_name" : "Tom",
"birth_date" : "2005-12-12",
"is_child" : "0"
}
},
{
"type": "persons",
"fields": {
"id" : "2",
"first_name" : "Bill",
"birth_date" : "2001-03-1",
"is_child" : "1"
}
}
]
The initial_data.json file can contain populate multiple resource model types
from the same file. Just ensure that the appropriate type value is specified.
initial_data.json file, it is
sanitized. This means it is validated and in process converted to a native
Java™ type. It is then stored in the backing database. Some fields are flexible
in what data it can handle. For instance, the boolean type can take, not only
actual boolean values, but also 0, '0', 'false' for false values and 1, '1',
'true' for true values. If initial_data.json has any of these alternate
values, it will be saved as boolean and subsequent access will reflect its
sanitized conversion.
Table customization
You can customize the table name against which ZRM generates and maps your
resource declaration. For example, if your application sets the resource type
name to be articles but needs the database table name to be
magazine_editorials, then you can specify the following in your resource model
file:
{
"db_table": "magazine_editorials",
"fields": {
"title":{"type": "string"},
"publish_date":{"type": "date"},
"body":{"type": "string", "format": "large"}
}
}