When the persistence engines (structure and content) of [fleXive] were designed, we tried to solve this problem by using a generic approach (unlike hibernate, which accesses data instances with (auto)generated classes). Since we wanted to use hierarchical data structures which are quite like XML in nature, the weapon of choice was an XPath-like approach: Values are accessed using their XPath.
As a simple example lets consider the following XML file:
<?xml version=“1.0“ standalone=“yes“?> <Person> <Name>Max Muster</Name> <Phone>+43 1 12345</Phone> <Phone>+43 1 800 FLEXIVE</Phone> <Address> <Street>Private road</Street> </Address> <Address> <Street>Office lane</Street> </Address> </Person>
With [fleXive] we would create a type called Person. We can use the Java API or a custom Groovy builder, the GroovyTypeBuilder, which resembles the hierarchic structure of the type more closely.
Example 6.13. Creating a "Person" type in Java
// create an editable type instance final FxTypeEdit type = FxTypeEdit.createNew("Person").save(); // create and save a new property for our person type type.addProperty("name", FxDataType.String1024); // create, modify, and save a new property for our person type type.addProperty("phone", FxDataType.String1024).setMultiplicity(new FxMultiplicity(1, 2)).save(); // create a new group type.addGroup("Address"); // add a new property for this group type.addProperty("Address/street", FxDataType.String1024); // the structures have been created and can now be used for creating contents
Example 6.14. Creating a "Person" type in Groovy
import com.flexive.shared.scripting.groovy.* import com.flexive.shared.value.* import com.flexive.shared.security.* import com.flexive.shared.structure.* import com.flexive.shared.* new GroovyTypeBuilder().Person { name(FxDataType.String1024) phone(FxDataType.String1024, multiplicity: new FxMultiplicity(1,2)) Address(multiplicity: FxMultiplicity.MULT_1_N) { street(FxDataType.String1024) } } def person = CacheAdmin.environment.getType("Person")The groovy code is pretty self-explanatory thanks to the concept of groovy builders. By convention property names are lowercase while groupnames are uppercase. See the section called “Structure Engine” for more information about structures.
Lets have a look at the following table, which is based on the XML data example, to visualize the mapping of XPath's to values:
XPath | Value |
---|---|
PERSON/NAME[1] | Max Muster |
PERSON/PHONE[1] | +43 1 12345 |
PERSON/PHONE[2] | +43 1 800 FLEXIVE |
PERSON/ADDRESS[1]/STREET[1] | Private road |
PERSON/ADDRESS[2]/STREET[1] | Office lane |
The XPath starts with the name of the type (which is optional if addressing in a
FxContent
instance, since there the type if obviously known but is needed for example in query results) and the path
to
address the property. Please note that XPaths are not case-sensitive and that an index of 1 is optional:
PERSON/ADDRESS[2]/STREET[1]
for example is identical to
PERSON/ADDRESS[2]/STREET
.
FxContent
serves as a container for groups, properties and general information about a content instance. It is used to
initialize (create an empty content instance), create, save and load content instances
[2]
.
Example 6.15. Creating "Person" instances
import com.flexive.shared.value.* import com.flexive.shared.content.* import com.flexive.shared.* def ce = EJBLookup.contentEngine def person = CacheAdmin.environment.getType("Person") FxContent co = ce.initialize(person.id) // co.setValue("/Name", "John Doe") co.setValue("/Phone", "+43 1 12345") // co.setValue("/Phone[2]", "+43 1 800 FLEXIVE") // co.setValue("/Address/Street", "Private road") // co.setValue("/Address[2]/Street", "Office lane") // FxPK pk = ce.save(co) // FxContent loaded = ce.load(pk) // FxString street = (FxString)loaded.getValue("/Address[2]/Street") // println "Loaded street: ${loaded.getValue("/Address[2]/Street")}" street.setValue("Office lane") // println "New street: ${loaded.getValue("/Address[2]/Street")}" ce.save(loaded) ce.remove(pk) //Copy and pasting the groovy code above into the groovy console will yield the following result on the application servers stdout after execution:
11:13:48,898 INFO [STDOUT] Loaded street: Ofice lane 11:13:48,899 INFO [STDOUT] New street: Office lane
The purpose of this example is to demonstrate how easy it is to create, update and remove content instances.
Since a content instance is tied to a type (See
the section called “Types”
for more information) the first thing that needs to be done is to initialize a new (empty) content instance
. Initializing a content instance creates as many property or group entries as
defined in the respective
assignments'
default multiplicity. Setting a value to a property assignment
is done by creating a new
FxValue
instance corresponding to the property's
data type
for the XPath of the assignment. The same applies if a group is involved, as shown in
for the street property of the first address group. Adding a new index is done by simply setting the XPath
to the desired value like in
or
. Please note that using an index of 3 would be illegal if no index of 2
exists.
Removing an XPath is as simple as calling
co.remove("/Address[2]")
if the second address group should be removed.
A content is created
and updated by calling the
ContentEngine
's
FxPK save(FxContent content)
method which returns the
primary key
of a content instance. Loading is done by providing the primary key to the
ContentEngine
's
load(FxPK pk)
-method.
.
Changing a value can be done by assigning a new
FxValue
instance (like in
) or by reading the current value
,
and changing it
which is updated in the content instance as can be seen from the
println
command on the application servers console.
To remove a content instance () only the primary key - and all required permissions - is needed.
To be able to identify a content, a primary key (implemented in
FxPK
) is needed. This primary key consists of the unique identifier (usually
equivalent to the id the content is stored in the database with) and the version. Depending on the
configuration
of the
type
the content belongs to it is possible to create different versions of a content.
A primary key can consist of a distinct version (a numerical value) or a predefined constant:
MAX
: Constant to select the maximum (highest) available version
LIVE
: Constant to select the version whose workflow step is flagged as
live
To create a new version simply call the
ContentEngine
's
createNewVersion(FxContent co)
method.
Information about all versions of a content can be retrieved using the
ContentEngine
's
FxContentVersionInfo getContentVersionInfo(FxPK id)
method.
FxContentVersionInfo
contains information about the minimum-, maximum- and most recently modified version numbers as well as
information which user was the last to update each version and if a live version exists.
[2] The term
content instance
is used to describe all data related to a concrete instance of a
FxType
where values are assigned to properties. A good analogy would be the instantiation (
FxContent
) of a class (
the section called “Structure Engine”
FxType
).