Resource model declaration

The resource model is the single, definitive source of metadata about your data. It contains declarations about the essential fields and behaviors of the data in your application.

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

This example model defines a persons resource model that has 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.json but can be overridden. See the following table customization section.
  • An id field is added automatically. Keys are generated by default and begin at 100, incrementing by 1.
  • An updated field is added automatically.
  • The CREATE TABLE SQL in this example is formatted using Derby syntax. Currently DB2, Derby, MySQL, and Oracle are supported and syntax will be selected based on the configured database in zero.config.

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

Three restrictions exist on resource model field names:

  • A field name cannot be a Java, Groovy, or SQL reserved word.
  • At present, a field name cannot contain more than one underscore in a row, due to the way the query lookup syntax works.

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.

Table 2 - Valid parameters for string types
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, and region.

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.

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.

Table 3 - Valid parameters for date types
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.

Table 4 - Valid parameters for decimal types
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() and update() on a Collection
  • validate() on a Member instance
  • POST and PUT on the HTTP resource
Empty string-based values are stored as empty strings, not as 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"}
If none is provided, ZRM will attempt to create a label from the field name. For instance, a field named "first_name" has the label value of "First name" and "birthDate" is "Birth date".

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"}
The value of 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 task from the command line. This task 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 task from the command line:

zero model sync
The model sync command line task will never alter any existing tables. The reason for this decision is because there is some level of ambiguity in altering existing tables that can lead to the loss of data. For instance, if you have a ZRM model declaration with a field named 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 task 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 task.

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.

The virtualized directory concepts are ignored in this case.

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.

When data is loaded from the 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"}
    }
}

Version 1.1.0.0.21442