Domain classes
A domain class represents a table column and it allows you to handle the column value as a Java object. In the Doma framework, a domain means all the values which a data type may contain. In short, a domain class is a user defined class that can be map to a column. The use of the domain classes is optional.
Every domain class is either an internal domain class or an external domain class.
Internal domain classes
The internal domain class must be annotated with @Domain
.
The @Domain
’s valueType
element corresponds to a data type of a column.
Specify any type of Basic classes to the valueType
element.
Instantiation with a constructor
The default value of the @Domain
’s factoryMethod
element is new
.
The value new
means that the object of annotated class is created with a constructor.
@Domain(valueType = String.class)
public class PhoneNumber {
private final String value;
public PhoneNumber(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String getAreaCode() {
...
}
}
Note
In Java 14 and later version, you can annotate records with @Domain
:
@Domain(valueType = String.class, accessorMethod = "value")
public record PhoneNumber(String value) {
public String getAreaCode() {
...
}
}
Warning
To annotate records with @Domain
is a little redundant,
because you must specify some properties to @Domain
such as valueType
.
Instead of @Domain
, you can annotate records with @DataType
:
@DataType
public record PhoneNumber(String value) {
public String getAreaCode() {
...
}
}
Instantiation with a static factory method
To create the object of annotated class with a static factory method,
specify the method name to the @Domain
’s factoryMethod
element.
The method must be static and non-private:
@Domain(valueType = String.class, factoryMethod = "of")
public class PhoneNumber {
private final String value;
private PhoneNumber(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String getAreaCode() {
...
}
public static PhoneNumber of(String value) {
return new PhoneNumber(value);
}
}
With a static factory method, you can apply the @Domain
annotation to enum types:
@Domain(valueType = String.class, factoryMethod = "of")
public enum JobType {
SALESMAN("10"),
MANAGER("20"),
ANALYST("30"),
PRESIDENT("40"),
CLERK("50");
private final String value;
private JobType(String value) {
this.value = value;
}
public static JobType of(String value) {
for (JobType jobType : JobType.values()) {
if (jobType.value.equals(value)) {
return jobType;
}
}
throw new IllegalArgumentException(value);
}
public String getValue() {
return value;
}
}
Using type parameters in internal domain classes
All internal domain class declarations have type parameters:
@Domain(valueType = int.class)
public class Identity<T> {
private final int value;
public Identity(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
When you create the object of annotated class with a static factory method, the method declaration must have same type parameters that are declared in the class declaration:
@Domain(valueType = int.class, factoryMethod = "of")
public class Identity<T> {
private final int value;
private Identity(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static <T> Identity<T> of(int value) {
return new Identity<T>(value);
}
}
External domain classes
This feature allows you to define any class as a domain class,
even if you cannot annotate the class with the @Domain
annotation.
To define external domain classes, you have to create a class that implements
org.seasar.doma.jdbc.domain.DomainConverter
and annotate @ExternalDomain
to the class.
Suppose, for instance, there is the PhoneNumber
class that you can change:
public class PhoneNumber {
private final String value;
public PhoneNumber(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String getAreaCode() {
...
}
}
To define the PhoneNumber
class as an external domain class, create following class:
@ExternalDomain
public class PhoneNumberConverter implements DomainConverter<PhoneNumber, String> {
public String fromDomainToValue(PhoneNumber domain) {
return domain.getValue();
}
public PhoneNumber fromValueToDomain(String value) {
if (value == null) {
return null;
}
return new PhoneNumber(value);
}
}
Using type parameters in external domain classes
All external domain class declarations have type parameters:
public class Identity<T> {
private final int value;
public Identity(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
In the DomainConverter
implementation class,
specify a wildcard ?
as type arguments to the external domain class:
@ExternalDomain
public class IdentityConverter implements DomainConverter<Identity<?>, String> {
public String fromDomainToValue(Identity<?> domain) {
return domain.getValue();
}
@SuppressWarnings("rawtypes")
public Identity<?> fromValueToDomain(String value) {
if (value == null) {
return null;
}
return new Identity(value);
}
}
Direct mapping of external domain classes to the database
All external domain classes can be directly mapped to any database type.
Here’s an example of mapping java.util.UUID
to PostgreSQL’s UUID type.
First, create an implementation of org.seasar.doma.jdbc.type.JdbcType
to handle the mapping:
class PostgresUUIDJdbcType extends AbstractJdbcType<UUID> {
protected PostgresUUIDJdbcType() {
super(Types.OTHER);
}
@Override
protected UUID doGetValue(ResultSet resultSet, int index) throws SQLException {
String value = resultSet.getString(index);
return value == null ? null : UUID.fromString(value);
}
@Override
protected void doSetValue(PreparedStatement preparedStatement, int index, UUID value)
throws SQLException {
preparedStatement.setObject(index, value, type);
}
@Override
protected UUID doGetValue(CallableStatement callableStatement, int index) throws SQLException {
String value = callableStatement.getString(index);
return value == null ? null : UUID.fromString(value);
}
@Override
protected String doConvertToLogFormat(UUID value) {
return value.toString();
}
}
Then, create a class that extends org.seasar.doma.it.domain.JdbcTypeProvider
,
and in the getJdbcType
method, return an instance of the JdbcType
implementation created above:
@ExternalDomain
public class PostgresUUIDConverter extends JdbcTypeProvider<UUID> {
private static final PostgresUUIDJdbcType jdbcType = new PostgresUUIDJdbcType();
@Override
public JdbcType<UUID> getJdbcType() {
return jdbcType;
}
}
Don’t forget to annotate the class with @ExternalDomain
.
Example
The Domain classes showed above are used as follows:
@Entity
public class Employee {
@Id
Identity<Employee> employeeId;
String employeeName;
PhoneNumber phoneNumber;
JobType jobType;
@Version
Integer versionNo();
...
}
@Dao
public interface EmployeeDao {
@Select
Employee selectById(Identity<Employee> employeeId);
@Select
Employee selectByPhoneNumber(PhoneNumber phoneNumber);
@Select
List<PhoneNumber> selectAllPhoneNumber();
@Select
Employee selectByJobType(JobType jobType);
@Select
List<JobType> selectAllJobTypes();
}