Datatype 接口
字符串格式本地化
许多标准数据类型使用 消息包 中定义的字符串格式。可以根据当前用户的区域设置进行格式化和解析。框架中默认的字符串格式如下:
# Date/time formats
dateFormat = dd/MM/yyyy
dateTimeFormat = dd/MM/yyyy HH:mm
offsetDateTimeFormat = dd/MM/yyyy HH:mm Z
timeFormat = HH:mm
offsetTimeFormat = HH:mm Z
# Number formats
integerFormat = #,##0
doubleFormat = #,##0.###
decimalFormat = #,##0.##
# Number separators
numberDecimalSeparator = .
numberGroupingSeparator = ,
# Booleans
trueString = True
falseString = False
如需提供自定义的字符串格式,需要将相应的消息添加到应用程序的消息包中。例如,要为英语区域设置美国日期格式,将以下行添加到你的 messages_en.properties 文件中:
dateFormat = MM/dd/yyyy
dateTimeFormat = MM/dd/yyyy HH:mm
offsetDateTimeFormat = MM/dd/yyyy HH:mm Z
或者,定义一个单独的 en_US 区域环境并在 messages_en_US.properties 文件中设置字符串格式。
| 你可以使用 Studio 配置字符串格式:打开 Project Properties 窗口的 Locales 选项卡,然后勾选 Show data format strings 复选框。 |
定制格式和解析
你可以将自己创建的数据类型分配给属性,实现特定实体属性值的自定义格式和解析。
假设你的应用程序中的某些实体属性存储日历的年份,由 integer 表示。用户应该能够查看和编辑年份,如果用户只输入两位数字,应用程序应将其转换为 2000 到 2100 之间的年份。否则,将输入的整个数字视为年份。
首先,实现 Datatype 接口并用 @DatatypeDef 注解:
import com.google.common.base.Strings;
import io.jmix.core.metamodel.annotation.DatatypeDef;
import io.jmix.core.metamodel.annotation.Ddl;
import io.jmix.core.metamodel.datatype.Datatype;
import org.springframework.lang.Nullable;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Locale;
@DatatypeDef(
id = "year", (1)
javaClass = Integer.class (2)
)
@Ddl("int")
public class YearDatatype implements Datatype<Integer> {
private static final String PATTERN = "##00";
@Override
public String format(@Nullable Object value) { (3)
if (value == null)
return "";
DecimalFormat format = new DecimalFormat(PATTERN);
return format.format(value);
}
@Override
public String format(@Nullable Object value, Locale locale) { (4)
return format(value);
}
@Nullable
@Override
public Integer parse(@Nullable String value) throws ParseException { (5)
if (Strings.isNullOrEmpty(value))
return null;
DecimalFormat format = new DecimalFormat(PATTERN);
int year = format.parse(value).intValue();
if (year > 2100 || year < 0)
throw new ParseException("Invalid year", 0);
if (year < 100)
year += 2000;
return year;
}
@Nullable
@Override
public Integer parse(@Nullable String value, Locale locale) throws ParseException { (6)
return parse(value);
}
}
| 1 | 数据类型的唯一标识。 |
| 2 | 该数据类型处理的 Java 类。 |
| 3 | 默认的格式化(没有区域设置时)。进行系统级转换时使用此方法。 |
| 4 | 基于区域设置的格式化。在 UI 中时使用此方法。 |
| 5 | 默认解析方法。进行系统级转换时使用此方法。 |
| 6 | 基于区域设置的解析。在 UI 中时使用此方法。 |
创建 Datatype 实现后,你可以使用 @PropertyDatatype 注解指定给实体属性:
@PropertyDatatype("year")
@Column(name = "YEAR_")
private Integer productionYear;
|
如果需要的话,注入 |
DatatypeFormatter
DatatypeFormatter 是一个工具 bean,用于在编程式对不同的数据类型值进行格式化和解析。会自动应用当前用户语言环境的 本地化格式字符串。
示例:
import io.jmix.core.metamodel.datatype.DatatypeFormatter;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.time.LocalDate;
@Component
public class DataFormatterService {
private final DatatypeFormatter datatypeFormatter;
public DataFormatterService(DatatypeFormatter datatypeFormatter) {
this.datatypeFormatter = datatypeFormatter;
}
public String formatDate (LocalDate date) {
return datatypeFormatter.formatLocalDate(date);
}
public LocalDate parseDate(String value) throws ParseException {
return datatypeFormatter.parseLocalDate(value);
}
}
自定义 Java 类型
你可以使用自定义的 Java 类作为实体属性的类型。
假设你创建了一个表示地理坐标的 Java 类:
import java.io.Serializable;
public record GeoPoint(double latitude, double longitude) implements Serializable {
}
现在你想将它用作 JPA 实体属性的类型。
首先,为你的类创建一个 JPA 转换器:
import com.company.demo.entity.GeoPoint;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter(autoApply = true) (1)
public class GeoPointConverter implements AttributeConverter<GeoPoint, String> {
@Override
public String convertToDatabaseColumn(GeoPoint attribute) {
if (attribute == null)
return null;
return attribute.latitude() + "|" + attribute.longitude();
}
@Override
public GeoPoint convertToEntityAttribute(String dbData) {
if (dbData == null)
return null;
String[] strings = dbData.split("\\|");
return new GeoPoint(Double.parseDouble(strings[0]), Double.parseDouble(strings[1]));
}
}
| 1 | 使用 autoApply = true 以后,你不需要在每个属性上都指定转换器。转换器将应用于对应类型的所有属性。 |
然后为 GeoPoint 实现 Datatype 接口,并用 @DatatypeDef 标注:
import com.company.demo.entity.GeoPoint;
import io.jmix.core.metamodel.annotation.DatatypeDef;
import io.jmix.core.metamodel.annotation.Ddl;
import io.jmix.core.metamodel.datatype.Datatype;
import org.springframework.lang.Nullable;
import java.text.ParseException;
import java.util.Locale;
@DatatypeDef(
id = "geoPoint", (1)
javaClass = GeoPoint.class, (2)
defaultForClass = true (3)
)
@Ddl("varchar(255)") (4)
public class GeoPointDatatype implements Datatype<GeoPoint> {
@Override
public String format(@Nullable Object value) { (5)
if (value instanceof GeoPoint) {
return ((GeoPoint) value).latitude() + "|" + ((GeoPoint) value).longitude();
}
return null;
}
@Override
public String format(@Nullable Object value, Locale locale) { (6)
return format(value);
}
@Nullable
@Override
public GeoPoint parse(@Nullable String value) throws ParseException { (7)
if (value == null)
return null;
String[] strings = value.split("\\|");
try {
return new GeoPoint(Double.parseDouble(strings[0]), Double.parseDouble(strings[1]));
} catch (Exception e) {
throw new ParseException(String.format("Cannot parse %s as GeoPoint: %s", value, e), 0);
}
}
@Nullable
@Override
public GeoPoint parse(@Nullable String value, Locale locale) throws ParseException { (8)
return parse(value);
}
}
| 1 | 数据类型的唯一标。 |
| 2 | 该数据类型所处理的 Java 类。 |
| 3 | defaultForClass = true 表示数据类型将自动应用于所有 GeoPoint 类型的实体属性。 |
| 4 | @Ddl 注解,表示 Studio 应该在数据库为该类型的属性生成 varchar(255) 类型的列。参考 后续内容。 |
| 5 | 默认的格式化(没有区域设置时)。进行系统级转换时使用此方法。 |
| 6 | 基于区域设置的格式化。在 UI 中时使用此方法。 |
| 7 | 默认解析方法。进行系统级转换时使用此方法。 |
| 8 | 基于区域设置的解析。在 UI 中时使用此方法。 |
之后,当你定义 GeoPoint 类型的实体属性时,框架将使用你定制的 JPA 转换器和数据类型:
@Column(name = "GEO_POINT")
private GeoPoint geoPoint;
@Ddl 注解
datatype 类的 @Ddl 注解中可以指定实体属性应该使用的 SQL 类型。Studio 会按照注解的配置生成 数据库迁移 脚本。
注解的 value 属性可以设置 SQL 类型,示例:
@DatatypeDef(id = "foo", javaClass = Foo.class, defaultForClass = true)
@Ddl("varchar(255)")
public class FooDatatype implements Datatype<Foo> {
如果需要为不同的数据库配置不同的 SQL 类型,可以添加多个 @Ddl 注解,通过 dbms 属性设置数据库类型。下面的示例中,同一个实体属性在 PostgreSQL 中使用 text 类型,在 Oracle 中使用 varchar2(255),而在其他数据库中,使用 varchar(255) 类型:
@DatatypeDef(id = "bar", javaClass = Bar.class, defaultForClass = true)
@Ddl("varchar(255)")
@Ddl(value = "text", dbms = "postgres")
@Ddl(value = "varchar2(255)", dbms = "oracle")
public class BarDatatype implements Datatype<Bar> {
Studio 支持以下 dbms 属性值:
-
hsql -
postgres -
mysql -
mssql -
mssql-2005 -
mssql-2008 -
mssql-2012 -
oracle