The Model
In Tentackle the model is neither XML- nor annotation-based but specified within special comment blocks, the so-called here-documents (did I hear anyone say Unix shell?). The syntax intentionally is very compact to allow grasping the model at a glance. You don’t need to scan the whole Java code for annotations and construct the model before your mind’s eye. For an example, open OrgUnit.java
and locate the appropriate comment block:
/* * @> $mapping * * # Organizational Unit * name := $classname * table := $tablename * inheritance := multi * integrity := $integrity * * ## attributes * [cached | +name] * String($ou_name) name name short name [key, uc, normtext] * String($ou_comment) comment ocomment optional comment [normtext] * * ## indexes * unique index udk := name * * ## relations * * ## validations * name: @NotNull(message="{ @('please enter the name') }") * * @< */
$mapping
, up to the end of the heredoc marked with @<
. Variables start with a dollar-sign and can be defined within the source file (like $tablename
, scroll up a bit…) or in the maven poms (like $integrity
, see section wurbletProperties
in the parent pom). Other variables like $classname
are predefined.Lines with a single hash are considered as comments related to the following model item and stored in the model, i.e.
"Organizational Unit"
is the comment for the entity OrgUnit
. Lines with a double hash are just comments to improve readability.As you already guessed, there are 5 logical sections:
- Header: containing such things as the entity name, tablename, inheritance type (in this case multi-table inheritance, also known as “table per class”), and the database integrity level (foreign key constraints).
- Attributes: defines the attributes (or properties) of the entity, one line per attribute. Options are enclosed in square brackets. Global options are on top and apply to all attributes or the entity.
- Indexes: the database indexes.
- Relations: the entities related to this entity. In this case: none.
- Validations: the validation rules or other annotations.
Let’s walk through the lines:
- Global option [cached | +name]:
- cached: OrgUnit is cached and other entities referring to OrgUnits will use the cache, wherever appropriate.
- | +name: When selecting all OrgUnits, they will be sorted by name.
- Attribute line consists of the Java type, the Java name, the database column name, the comment and options.
- Attribute name: is a
String
with a limited length of$ou_name
(20). This is a domain key, uppercase only and part of the normalized text (more on that later). - Attribute comment: accordingly.
- Unique index named
orgunit_udk
for the columnname
. - Validation of name: must not be null. If it is null, a validation result with the I18N message-key
"please enter the name"
is created.
Java code
Next, let’s examine the interface declaration below the model:
While the annotations are pretty self-explaining, the interface declaration requires some words. At first glance, the generics may look a little weird.
What does OrgUnit<T extends OrgUnit<T>>
mean?
Let’s begin with T extends OrgUnit<T>
: that’s a so-called “self-bound” or “self-referential” generic type, explained here. It allows us to declare a method in OrgUnit
such as:
public T getSomeOrgUnit() { ... }
where T
is of type OrgUnit
. In subclasses such as User
this method will return a User
and we don’t need any casts. Please take a look at User
how this is done. The framework uses this trick in many places to avoid ugly casts and instanceof checks.
Anyway, you don’t need to code that manually. As you will learn in the next chapter, all the files related to an entity can be created with the help of a wizard and a nice GUI.
OrgUnit
itself does not contain any other declarations. They are defined in OrgUnitPersistence
for the persistence layer and OrgUnitDomain
for the domain layer. MasterData
is just a marker interface. Although not really necessary, it is good practice to distinguish between master- and transactiondata. Did you notice the database schemas md and td already? Of course, you’re not forced to do so in your applications. It’s not required by the framework and you can change the configuration in the parent pom, if you like.
Generated code
Below the interface declaration you will find two generated code blocks. The IDE has automatically collapsed the generated code and instead displays a short description of what’s inside. Tentackle uses the Wurbelizer to generate code. We will cover that later, but for now you should know that the code is generated by so-called wurblets: code generators embedded within the sources. Please expand the code named modelComment
:
/* * --------------------------------------------------------------------------- * * OrgUnit is referenced by: * * Message via orgUnitId as orgUnit [1:1] * * * OrgUnit is a root entity * * * OrgUnit is not referencing other entities * * * Components of OrgUnit are not deeply referenced * * * OrgUnit is extended by: * ^ User * ^ UserGroup * * --------------------------------------------------------------------------- */
The wurblet generated a comment that gives you a quick overview of how the entity is used in your project.
You may open the other files related to OrgUnit
:
– OrgUnitPersistence
: the interface declaring persistence methods and constants.
– OrgUnitPersistenceImpl
: the persistence implementation.
– OrgUnitDomain
: the interface declaring the domain methods.
– OrgUnitDomainImpl
: the domain implementation.