Under normal circumstances, record data ceases to exit as soon as the model instance ceases to exist. If you want to persist Record instances, mark their Records as shared: shared Records and their Relationships are reflected in the database as tables.
When you create an instance of a shared Record, a database entry is created in the database table: any readings, modifications, and deletions of shared Record values are reflected in the entry.
Note: Note that variables of a shared Record type hold a reference to the shared record and must be fetched anew in each new transaction: You might want to consider the performance aspect (for details on model transactions, refer to the Software Development Kit Guide).
The persistence mechanism of shared Records makes use of Hibernate: Based on the data models, the system generates the respective tables and a single common Hibernate setting file: as a consequence, if you upload multiple versions of a data type hierarchy in multiple modules, only the last data type model is used.
If you modify the data type hierarchy, you have to decide how to handle the changes in the database mapping: this is set by the Database Schema Update Strategy (similar to the hbm2ddl
Hibernate configuration) of your server connection. You can also enable schema update per Record.
Each data type definition can specify properties that define the database where the tables are persisted. To change the properties of data types in a definition file, such as, target database, table names, foreign key names, and index name prefixes, do the following:
Database: JNDI name of the data source used for the database
This allows you to store the instances of shared Records in another database accessible to your application server.
Table name prefix: prefix used in the names of database tables created based on this data type definition
It is good practise to use a prefix so you can easily find your tables. You can check the prefixed table name for individual Records and Relationship in their Properties view.
If a data type definition contains shared Records with a common prefix or suffix, foreign key or index names on the DB Mapping tab of the Properties view, you can extract the affixes so that they are set for the entire data type definition.
To extract affixes from a database with shared records, do the following:
To generate a data type model from a database schema, do the following:
In the Generate Types from DB Schema dialog box, select the data source and click Next.
If the data source is not listed, click New and define its properties.
Select the tables and columns to be included in the data type model.
Note the following limitations:
The tool fails to detect that a shared record for a table already exists in the module and generates a new record.
Consequently, if a record that is being generated has a relationship to an already existing record, the relationship is not generated either.
Shared records are defined just like common Records with the additional shared flag, which is equivalent to @Entity
in Hibernate, and the following properties related to persisting:
Important: If multiple shared records have the same target table and schema, the shared records will be mapped to the same table. This might results in issue due to incompatible Schema incompatibilities. To prevent such issues, consider setting Table name prefix for your data type definition.
Important: When working with shared Records that are a target or source ends of a relationship, make sure to define indexes for the foreign keys on the Records and their Relationship to prevent potential performance issues. Note that you can generate the indexes in the data types file automatically: right-click the canvas of your Record diagram and select Generate Indexes.
To create a Record field in a shared Record, do the following:
Select the field and define its generic properties on the Detail tab of its Properties view.
The type of a shared field should be set to a simple data type. If such a field is of another data type, consider creating a related shared Record to prevent performance issues due to frequent serialization and deserialization.
Define the database-related attributes on the DB Mapping tab:
Note: When writing values into the fields, Decimal fields behave like Java's BigDecimal.
null
. If you try to create a record instance with the field set to null
, the operation will fail with an exception.Auto generated: if true, the field value is generated automatically when the instance of the shared record is created.
The attribute is available only for fields that are simple primary keys with integer values. Depending on the target database, either a sequence is generated, or auto-incrementation is used.
If it does not exist, it is be created.
Important: When creating a new record with a specified property that is set as auto-generated over an H2 database, the system will silently ignore the specified value and use the auto-generated value. For example, if a shared Record Book defines the field ID that is auto-generated and you instantiate a new Book as
new Book (id->1)
, the ID value 1 will be ignored and the auto-generated ID will be used instead. On other databases, such code causes an exception.
Locking prevents a change of a shared record if the record changed since it was loaded; for example, if you save a to-do with a shared record and another user changed the record from another to-do while you were editing it. In such a case, the attempt to apply the changes on the shared record will result in a transaction rollback and an exception.
Optimistic locking uses a dedicated Field of a shared record to store its Version: the Field has its value bumped each time the record changes. If you are changing a record with a particular version and the version changes in the meantime, the server returns an exception when you try to apply your changes. Note that you can exclude fields from optimistic locking: when such fields change, the version Field of the record remains unchanged. As a result, the field accept all its changes from all transactions without causing a transaction rollback.
If not selected, and the Field is changed by multiple users at once, the system returns a Conflict on entity
exception.
Note: The locking mechanism makes use of the optimistic locking mechanism of the underlying Hibernate framework.
To set up locking on a shared Record, do the following:
Optionally, exclude Fields which can be changed between their load and save:
On the DB Mapping tab of the Fields' Properties view, select Exclude from optimistic lock.
Data Relationships between Records establish a relationship between the Record tables. Unlike in JPA, the Relationship is symmetrical (set on both ends of the relationship) and it is not necessary to make one of the tables the Owner.
While values of shared Records are fetched anew in every transaction of a model instance, when you fetch a shared Record that is related to other records, the related Records can be fetched immediately or when explicitly requested. This is determined by the fetching strategy on the data relationship ends:
source.relationshipname
)To allow quick look-up of shared Records in relationships, create indexes of foreign keys for the underlying database tables: you can do so directly in the database or you can define the indexes using PDS.
Important: The absence of indexes for your shared Records can cause performance issues. Make sure to define indexes to prevent slow search on your database data.
To define indexes for a table of a shared Record from PDS, do the following:
To generate indexes on foreign keys for all shared records that are related to another record in the data types file, right-click the file in GO-BPMN Explorer and click the Generate Indexes button in the Properties view; alternatively you can right-click into the canvas in a Record diagram and select Generate Indexes.
To allow for a more efficient recovery of IDs of related shared Records, you can define the foreign key of the Relationship end as the column name of the record:
Now you can access the primary key of the related shared Record using the read-only Field.
Such foreign key fields, if set as primary keys, are set automatically when the related record is assigned. For example, if Parent has a relationship to Child and one of the Child's primary-key fields is mapped to the primary key of Parent, the field is automatically filled with the Parent id:
Note that you need to make the relationship with the parent object: do not assign the foreign key directly as, for example, new MyChild(id -> 1, parentId -> p1.id);
The auditing of shared records refers to storing of each version or revision of a shared record which is subject to auditing: for instances of such records, as the record changes, the system stores each revision. You can then work with individual versions of the entity using auditing functions.
When you change records of audited Records in a transaction, the auditing mechanism creates a revision entity with an ID and inserts "snapshots" of the changed records to their auditing tables. Optionally, it enters the Record name and revision ID for each changed record into the entity name table.
Example: Book is an audited Record with the Field title and you create a new book and edit an existing one:
new Book(title -> "Something Happened"); // record id is 2getBookByTitle("Catch 22").title := "Catch-22"; //record id is 1Auditing will perform the following:
- Create a revision with revision ID and optionally the timestamp as a Revision Entity, for example, with the ID
1
.- Record the changes on the Book instances: two entries, one for the new book and another one for the changed book with the following details:
- record ID
- revision ID set for both to
1
- type of change
- title of the book as after change
- Optionally, the entity name table records for each change a new entry with record type: hence two entries with record BOOK and the revision ID
1
To set up auditing, do the following:
Note: To store additional revision-related information, modify the underlying data model, that is, either add new Fields to the Revision Entity Record or create a related shared Record (refer to Entity Auditing in the Software Development Kit documentation). Important: Only one revision entity Record and modified entity name Record can exist on the LSPS server.
Note: By default the Revision Entity uses the LSPS implementation of the Revision Listener to enter revision data into the database table. The listener enters the id of the revision and optionally the timestamp into the database table. If you want the system to enter further data about the revision, you need to implement your own Revision Listener that will extend the LSPSRevisionListener class (refer to Entity Auditing in the Software Development Kit documentation).
To enable auditing of a shared Record, do the following:
On the Auditing tab:
By default, the table name is <RECORD_NAME>_AUD
.
Note: Make sure that you have uploaded the Revision Entity shared Record to your server: the LSPS database will contain the ENTITY_REVISION table.
If you want to exclude a Record Field from auditing, open its Properties and on the Auditing tab, select the Excluded from auditing option.
If you want to exclude a Record Relationship end from auditing, open the Relationship Properties and on the tab for the Relationship end, either the Source tab or the Target tab, select the Excluded from auditing option.
To work with revisions of Record instances, use the provided Standard Library functions available in the Core module:
The caching mechanisms for shared Records reduces the load on the underlying databases. It ensures that shared Records that might be required by the same model instance, user, as well as other users and transactions are kept in memory.
LSPS applies first-level caching within individual transactions, that is, any data is cached within a transactions. The cache regions implement second-level caching that is applied on shared Records. The cache exists regardless of the instance transaction or model instance life (refer to Software Development Kit Guide for information on transactions).
The caching is defined by cache regions, which are added to the LSPS Server cache on module upload.
To define a cache region, do the following:
To disable the LSPS system cache regions, define the disabled cache regions in the <YOUR_CUSTOM_APP>-ejb/src/main/resources/cache-regions.properties
file of your custom LSPS application.