Spring Data Sophora 4

Writing Models

This part of the reference documentation will guide you through the process of writing Java models for your Sophora documents.

This part of the reference documentation will guide you through the process of writing Java models for your Sophora documents.

Entities Library

Spring Data Sophora comes with some useful model classes to easily map some child node constellations. To use these model classes you have to add the following dependency to your project:

<dependency>
   <groupId>com.subshell.sophora</groupId>
   <artifactId>spring-data-sophora-entities</artifactId>
   <version>${spring.data.sophora.version}</version>
</dependency>

Model classes

A Spring Data Sophora Java class contains all fields (properties and child nodes) that shall be mapped from a Sophora document to the Java model. A model class is therefore the Java equivalent of the node type declaration (CND) and node type configuration of a specific Sophora node type.

The model class must contain at least the node type which must be declared as an annotation of the Java class. A minimal model class may look like this:

@NodeType(value = "sophora-example-nt:story")
public class MyStory extends SophoraDocumentEntity { }

This class will instantiate Java objects for all documents of the given node type sophora-example-nt:story. The example model will only provide fields that are inherited from the SophoraDocumentEntity class which are fields that all documents share e.g. UUID, Sophora ID, External ID etc.

Properties

In order to add properties to your Spring Data Sophora Java model you will have to declare an instance variable for each property. Since a property name also contains a namespace in the CND, that namespace must also be declared. This can be done via the annotation @PropertyNamespace which will then be used for every property within that model.

@NodeType(value = "sophora-example-nt:story")
@PropertyNamespace("sophora-example")
@Data
public class MyStory extends SophoraDocumentEntity {

    private String title;
    private String teaser;

}

The above example declares instance variables for the following corresponding properties:

  • sophora-example:title
  • sophora-example:teaser

@NodeType(value = "sophora-example-nt:story")
@PropertyNamespace("sophora-example")
@Data
public class MyStory extends SophoraDocumentEntity {

    @Property(namespace = "sophora-other")
    private String title;
    @Property("sophora-other:shorttext")
    private String teaser;

}

Your node type may consist of several properties of different types. The following table lists all supported mappings of a source type (CND) to their appropriate target type (Java variable).

Source Type (Property Type in CND)Target Type(s) (Java type of Model Variable)
booleanjava.lang.Boolean
double
  • java.lang.Double
  • java.lang.Float
long
  • java.lang.Integer
  • java.lang.Long
date
  • java.util.Date
  • java.time.Instant
  • java.time.ZonedDateTime
string
  • java.lang.String
  • com.subshell.sophora.commons.range.ParsedRange
  • com.subshell.sophora.spring.data.repository.entities.SingleSelectValue
  • com.subshell.sophora.spring.data.repository.entities.MultiSelectValue
  • custom class annotation with com.subshell.sophora.spring.data.annotations.SelectValue
  • com.subshell.sophora.spring.data.repository.entities.table.Table
referencecom.subshell.sophora.spring.data.repository.support.Reference

Select Values

Select values are basically just properties of type string. You may just map them to a String variable in Java which will then contain the actual value of the selected select value.
In some cases you may need more than just the value of the select value. For these cases we provide different model classes you might want to consider for your Java model.

SingleSelectValue

The SingleSelectValue class can be used when the property configuration of the select value property allows to choose exactly one value. The SingleSelectValue variable in your model class will then contain the chosen value as well as its label, description, icon and referenced document. Example:

@NodeType(value = "sophora-example-nt:story")
@PropertyNamespace("sophora-example")
@Data
public class MyStory extends SophoraDocumentEntity {

    private SingleSelectValue<?> contentType;

}

MultiSelectValue

The MultiSelectValue class can be used when the property configuration of the select value property allows multiple values. The MultiSelectValue variable in your model class will then contain the chosen values as well as their label, description, icon and referenced document. Example:

@NodeType(value = "sophora-example-nt:story")
@PropertyNamespace("sophora-example")
@Data
public class MyStory extends SophoraDocumentEntity {

    private MultiSelectValue<?> exportTargets;

}

@SelectValue

SingleSelectValue and MultiSelectValue are generic model classes to map select values. If you prefer a mapping of a fixed set of select values you may as well write your own class and annotate it with @SelectValue. If you do so, you will also have to provide a static method which performs the actual mapping from the string property to a value of the class. Example:

@SelectValue
public enum VideoContentType {

    WEBONLY,
    LIVESTREAM,
    UNKNOWN;

    @SophoraStringToSelectValue
    public static VideoContentType fromSelectValue(String value) {
        return Arrays.stream(VideoContentType.values())
            .filter(displayStyle -> displayStyle.toString()
                .equalsIgnoreCase(value))
            .findFirst()
            .orElse(UNKNOWN);
    }
}

You may then use your own select value class in another model class which contains the select value property:

@NodeType(value = "sophora-example-nt:video")
@PropertyNamespace("sophora-example")
@Data
public class MyVideo extends SophoraDocumentEntity {

    private VideoContentType contentType;

}

Tables

If you are using the "Table" input field type for your string property the actual value of the property will contain the XML that forms the table. In order to parse this XML to a Java model, Spring Data Sophora already comes with a Table class that you can use as type for your corresponding Java variable. Example:

@NodeType(value = "sophora-example-nt:story")
@PropertyNamespace("sophora-example")
@Data
public class MyStory extends SophoraDocumentEntity {

    private Table contributors;

}

References

Reference properties of a node type can be mapped to a special Reference model class:

@NodeType(value = "sophora-example-nt:movie")
@PropertyNamespace("sophora-example")
@Data
public class MyMovie extends SophoraDocumentEntity {

    @Property("trailer")
    private Reference<Video> trailerRef;

}

The Reference's generic type is the model class of the document type it is referencing. If you want to retrieve the referenced document, you may do it like this:

...
MyMovie movie = repository.getMovieFromSomewhere();
Reference<Video> trailerRef = movie.getTrailerRef();
if (trailerRef.isPresent()) {
    Video trailer = trailerRef.get();
    doStuffWithTrailer(trailer);
}
...

Child Nodes

Child nodes must be mapped differently from properties because they (like documents) have a node type with properties and arbitrary child nodes. It is therefore necessay to mimic the node type structure in your Java model classes. Let us assume we have a story document type that contains teaser images as well as teaser boxes with other referenced stories.

Since we want to include the teaser images and boxes in our story model we should start to write model classes for them first:

@NodeType(value = "sophora-extension-nt:image")
@PropertyNamespace("sophora-extension")
@Data
public class MyImage extends SophoraDocumentEntity {

	private String alttext;
	private String caption;

}

To easily reference the above image model we should create a reference class for it:

@NodeType("sophora-example-nt:imageRef")
@PropertyNamespace("sophora-extension")
@Data
public class MyImageRef extends ReferenceEntity<MyImage> {

    private String alttext;
    
}

We also need to create a model class for our teaser boxes that should be able to reference stories. Because a teaser box is a child node (not a document) it is inherited from the SophoraChildnodeEntity:

@NodeType("sophora-example-nt:teaserBox")
@PropertyNamespace("sophora-extension")
@Data
public class MyTeaserBox extends SophoraChildnodeEntity {

    private String title;

    @ChildNode("teaser")
    private List<ReferenceEntity<Story>> teasers;
    
}

Finally we can write the story model class itself and use all the above classes:

@NodeType(value = "sophora-example-nt:story")
@PropertyNamespace("sophora-example")
@Data
public class MyStory extends SophoraDocumentEntity {

    private String title;

    @ChildNode("teaserImage")
    private List<MyImageRef> teaserImages
    @ChildNode
    private List<MyTeaserBox> teaserBoxes

}

Reference Entities

The ReferenceEntity class, which we used some times in the example above, is a model class for the node type sophora-nt:reference. This node type already comes with your Sophora installation. Whenever you write your own reference node type you have to inherit from sophora-nt:reference.

This means that whenever you write a model class for your reference node type you will also have to inherit from the ReferenceEntity class.

While the Reference class can only be used for properties of type reference that will not have any meta data, the ReferenceEntity class also contains all fields to support overridden properties and time scheduling of referenced documents.

Mixins

Mixins may add additional properties or child nodes to an existing node type. In order to describe this addition in the corresponding Java model of a node type that contains a mixin, the @Mixin annotation can be used.

You may also use the @Mixin annotation without specifying a specific node type:

@Mixin("sophora-example-mix:taggable")
@PropertyNamespace("sophora-example")
@Data
public class MyTaggableDocument extends SophoraDocumentEntity {

    private List<String> tags;

}

Note that we skipped the @NodeType annotation in this case. Classes annotated with @Mixin will only be used as model classes when there is no explicit node type model class for the document to convert.

Last modified on 8/5/21

The content of this page is licensed under the CC BY 4.0 License. Code samples are licensed under the MIT License.

Icon