Mandatory vs optional
By default, Apache Causeway assumes that all properties of an domain object or view model are required (mandatory). The optionality() attribute allows this to be relaxed. The attribute is also supported for parameters.
That said, properties are most commonly defined on persistent domain objects (entities), in which case the JPA @Column should be specified. Apache Causeway can infer the maxLength directly from the equivalent @Column#length() annotation.
That said, properties are most commonly defined on persistent domain objects (entities), in which case the JPA @Column will in any case need to be specified.
Apache Causeway can infer the optionality semantic directly from the equivalent @Column#nullable() annotation/attribute.
For example:
import jakarta.persistence.Column;
import lombok.Getter;
import lombok.Setter;
public class Customer {
@Column(nullable=true)
@Getter @Setter
private String middleInitial;
// ...
}
In this case there is no need for the @Property#optionality attribute.
Mismatched defaults
If the @Column#nullable attribute is omitted and the `@Property#optionality() attribute is also omitted, then note that Causeway' defaults and JPA/Eclipselink's defaults differ.
Specifically, Causeway always assumes properties are mandatory, whereas JPA assumes instead that properties (both primitivess and reference types) are optional.
When Apache Causeway initializes it checks for these mismatches during its metamodel validation phase, and will fail to boot ("fail-fast") if there is a mismatch.
The fix is usually to add the @Column#nullable() annotation/attribute.
'Single table' inheritance type
There is one case (at least) it may be necessary to annotate the property with both @Column#nullable and also @Property#optionality().
If the property is logically mandatory and is in a subclass, but the mapping of the class hierarchy is to store both the superclass and subclass(es) into a single table (ie a "roll-up" mapping using jakarta.persistence.InheritanceType#SINGLE_TABLE), then JPA/Eclipselink requires that the property is annotated as @Column#nullable=true: its value will be not defined for other subclasses.
In this case we therefore require both annotations.
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
@Entity
@Inheritance(type = InheritanceType.SINGLE_TABLE)
public abstract class PaymentMethod {
...
}
@Entity
@Inheritance(type = InheritanceType.SINGLE_TABLE)
public class CreditCardPaymentMethod extends PaymentMethod {
private String cardNumber;
@Column(nullable=true)
@Property(optionality=Optionality.MANDATORY)
@Getter @Setter
private String cardNumber;
// ...
}
Non-persistent properties
Of course, not every property is persistent (it could instead be derived), and neither is every domain object an entity (it could be a view model).
For these non persistable properties the optionality attribute is still required.
For example:
import jakarta.persistence.Transient;
public class Customer {
@Transient (1)
@Property(optionality=Optionality.OPTIONAL)
public String getFullName() { (2)
// ...
}
public void setFullName(String fullName) { (3)
// ...
}
// ...
}
| 1 | indicates that this property is not persisted |
| 2 | implementation would most likely derive full name from other persisted properties (eg first name, middle initial, last name) |
| 3 | if supported, implementation would most likely parse the input and update the underlying persisted properties |
|
The attribute has no meaning for a primitive type such as |
The values for the attribute are simply OPTIONAL or MANDATORY.
|
It is also possible to specify optionality using @Nullable annotation. |