-
Notifications
You must be signed in to change notification settings - Fork 4
Data Model
A 4DFLib ready model class extends CommonState.
Lets break up the name, because there are 2 functions served by CommonState:
There are 9 properties that children of CommonState will inherit:
These 9 properties are persisted as fields in every database table that 4DFLib manages. The library automatically creates and manages a table for each model class that is passed in the List to FdfServices.initializeFdfDataModel(List passedClasses). These model class must extend CommonState and have a default constructor see: FdfServices Classes that are added to the initialization array and extend CommonState with a default constructor will be referred to as "4DFLib model classes".
Here is a bit more detail on each and what it's purpose is:
public long rid; // rowId, every state will have unique one, primary key for all entities
public long id; // entity / record id, each entity will have a unique id, which may have multiple rowIds
public boolean cf; // current flag, 1 = current state / 0 = history
public boolean df; // deleted flag, 1 = deleted state / 0 = not deleted
public Date arsd; // active record start date
public Date ared; // active record end date
public long euid; // edit user id
public long esid; // edit system id
public long tid; // tenant id
rid: Row ID -> This is the classic auto-incrementing primary key for the row, it is actually not necessary, as the combination of id and other fields above is still a unique composite key. But I think for the sake of discussion here it is worth using the rid.
id: ID -> This is the actual numerical identifier for the entity, this is what will be used as a foreign key to represent the entity and will be the same in every row related to the same entity (the rid will be unique). Having rid and id fields in every table will likely prove to a be a huge advantage when you are migrating data from one system to another and want to maintain original id/key values for your data.
cf: Current Flag -> a Boolean value that makes separating historical and current data easier in queries. A value of 1 is current, 0 or null is not. It is not actually necessary, the same information could be gathered by looking to see if the ared is null for current or has a valid date for historical, but I believe there is real value in having it, especially when you consider the ease of querying the data.
df: Deleted Flag -> this is how you can remove mistakes and bad data from showing up in future queries.
arsd: Active Record Start Date -> Timestamp representing the time in milliseconds that the record was inserted into the table, this marks the start of the “active lifespan” or window of time in which this row is considered the current row for the entity.
ared: Active Record End Date -> Timestamp representing the time in milliseconds that the record was superseded by another row and entered “historical” status. If this value is null the row is still the current row for the entity.
euid: Edit User ID -> Foreign key to the user that inserted this row
esid: Edit System ID -> Foreign key to the system (there is a lookup table with more information on the system) that the edit user was using when they made the insert of the row. This value might represent a primary application interface for the datastore, or maybe a data-load or batch-load process.
tid: Tenant ID -> Allows 4DFLib to support multi-tenancy natively. If your system does not have a multi-tenancy requirement, you can simply ignore this field and the overloaded methods we will discuss later in the Services to support saving and querying data by tenant.
In addition to these 9 inherited properties, 4DFLib will automatically create columns in the database for any public properties that are not marked with a @FdfIgonre annotation in your 4DFLib model classes. More on the FdfIgnore annotation later.
Here is a simple example of how these properties are persisted in a normal database table. We will use a 4DFLib model class called User to illustrate how it all works. Note that we are working with SQL queries in the following examples only to show how the data is persisted by the library so you can see how everything works. You will not need to write SQL queries in your application.
In addition to the 9 properties inherited with CommonState, the User class has firstName, lastName and middleName properties.
Here are some examples of how you would directly query the data in a 4DFLib database directly. Note that 4DFLib manages it's own ORM layer and you can use / extend FdfCommonServices to interact with data from your application, see WhereStatement for more details on building custom ORM interactions.
If we wanted to query only current records in the first tenant (note all queries below assume the first tenant only):
select * from users where cf=1 and df=0 and tid=1;
/* Note df=0 to ensure no deleted rows are returned */
And to get all the records for a particular user (in this case Donald Duck) throughout time we can query, this will show us all of the history for the Donald duck user in the first tenant
select * from users where id=4 and df=0 and tid=1;
Now let us say that you want to see the state of all entities at a specific time, lets say that time is 03/21/2015 15:33:50.
select * from users where (‘2015-03-21 15:33:50’ between arsd and ared or (arsd < ‘2015-03-21 15:33:50’ and ared is null)) and df=0 and tid=1;
Giving the following result set:
There you have it, a quick and simple way to see how everything "was" at the time given. This is barely scratching the surface of course of the possibilities here and I am giving example SQL statements for illustration purposes only, as you will see in the next parts working our way up the layers, these queries are already available to all data Entities.
The upcoming services section of the documentation will help describe how you query and save data within your application using the 4DFLib APIs.
As me mentioned at the start of this page, CommonState's name has 2 significant parts, Common and State. We have talked about how the first half, Common, describes the common properties to any 4DFLib model object. Now we are going to explore what the second part of the name, State, means.
We mentioned in the past that 4DFLib model classes persist all history automatically. In order to do that you need to make the concept of the entity slightly more complex to account for changes in state over time. The way 4DFLib accomplishes this is by using the concept of an entity that has states. At the database level, you can think of a state as a single row with a unique rid, and an entity as a grouping of rows that all have the same id.
To understand this better, let up take a look at FdfEntity. As you see, an FdfEntity has the following properties:
public S current;
public List<S> history;
public long entityId = -1;
Note that the Type Parameter S signifies the State associated with the Entity. You must instantiate your Entity with a State as a type. So for example if we were to create an entity for our User 4DFLib model object, it would look like the following:
FdfEntity<User> user;
Note that our User entity has a current state (the record with a cf=1 and df!=0) and List of history states. Anytime you ask a 4DFLib API for full historical data (withHistory) you can expect to get back a FdfEntity<S extends CommonState>
where S is your state type. If you do not need all the history you can use the API calls without (withHistory) and you will get back just <S extends CommonState>
or your class. In our case we would simply get back a User object.