7.1 创建自己的类型转换器
有时,当您想在表的一列中存储相当复杂的数据类型时,会出现这种情况。如果 Hibernate 知道如何将它转换为字符串(并返回),那么一切都很好。如果没有,那么您将不得不编写自己的数据转换器。
假设某人决定将用户的出生年份存储在数据库中ГГ.ММ.ДД
,例如:98.12.15
。您还需要将其转换为常规日期:15/12/1998
。然后你必须编写自己的转换器。
为此,您需要实现一个接口AttributeConverter<EntityType, DbType>
。
@Converter(autoApply = true)
public class DateConverter implements AttributeConverter<java.time.LocalDate, String> {
public String convertToDatabaseColumn(java.time.LocalDate date) {
return date.format("YY.MM.DD");
}
public java.time.LocalDate convertToEntityAttribute(String dbData) {
String[] data = dbData.split(".");
return LocalDate.of(data[2], data[1], "19"+data[0]);
}
}
当然,这个转换器可以添加到任何字段(前提是类型匹配):
@Entity
@Table(name="user")
class User {
@Id
@Column(name="id")
public Integer id;
@Column(name="join_date")
@Convert(converter = DateConverter.class)
public java.time.LocalDate date;
}
如果您没有设计数据库,则通常必须使用转换器。那里的数据可能是“奇怪的格式”。日期可以存储为字符串,布尔值可以存储为具有 Y 和 N 值的 CHAR,等等。
7.2 创建我们自己的数据类型
还记得带有 Hibernate 已知类型列表的表格吗?我说的是与注释一起指定的类型@Type
。您可以编写自己的数据类型,它可以像 Hibernate 中的其他内置类型一样使用。
例如,我们想要 LocalTime 类型,它在数据库中存储的不是 TIME,而是 VARCHAR。并且,例如,我们可以访问这样的数据库,并且不允许我们更改其列中的数据类型。然后我们可以编写自己的 Hibernate 类型。我们称它为 LocalTimeString。
首先我们需要一个小类来描述我们的新类型:
public class LocalTimeStringType extends AbstractSingleColumnStandardBasicType<<LocalTime> {
public static final LocalTimeStringType INSTANCE = new LocalTimeStringType ();
public LocalTimeStringType () {
super(VarcharTypeDescriptor.INSTANCE, LocalTimeStringJavaDescriptor.INSTANCE);
}
@Override
public String getName() {
return "LocalTimeString";
}
}
它是一种由一个值组成的枚举类型。此类单个 enams 的集合是 Hibernate 已知的所有类型。
我们还需要一个类 - 转换器的模拟,它将包含两个方法 -wrap()
并将unwrap()
LocalTime 类型的值转换为 String。
这是不执行这些方法时的样子:
public class LocalTimeStringJavaDescriptor extends AbstractTypeDescriptor<LocalTime> {
public static final LocalTimeStringJavaDescriptor INSTANCE = new LocalTimeStringJavaDescriptor();
public LocalTimeStringJavaDescriptor() {
super(LocalTime.class, ImmutableMutabilityPlan.INSTANCE);
}
public <X> X unwrap(LocalTime value, Class<X> type, WrapperOptions options) {
}
public <X> LocalTime wrap(X value, WrapperOptions options) {
}
}
现在让我们编写方法的实现:
public <X> X unwrap(LocalTime value, Class<X> type, WrapperOptions options) {
if (value == null)
return null;
if (String.class.isAssignableFrom(type))
return (X) LocalTimeType.FORMATTER.format(value);
throw unknownUnwrap(type);
}
第二种方法:
@Override
public <X> LocalTime wrap(X value, WrapperOptions options) {
if (value == null)
return null;
if(String.class.isInstance(value))
return LocalTime.from(LocalTimeType.FORMATTER.parse((CharSequence) value));
throw unknownWrap(value.getClass());
}
准备好。您可以使用此类将时间存储为字符串:
@Entity
@Table(name="user")
class User
{
@Id
@Column(name="id")
public Integer id;
@Column(name="join_time")
@Type(type = "com.codegym.hibernate.customtypes.LocalTimeStringType")
public java.time.LocalTime time;
}
7.3 注册你的类型
您还可以在 Hibernate 配置期间注册您的数据类型。这有点不平凡。
ServiceRegistry serviceRegistry = StandardServiceRegistryBuilder()
.applySettings(getProperties()).build();
MetadataSources metadataSources = new MetadataSources(serviceRegistry);
Metadata metadata = metadataSources
.addAnnotatedClass(User.class)
.getMetadataBuilder()
.applyBasicType(LocalTimeStringType.INSTANCE)
.build();
SessionFactory factory = metadata.buildSessionFactory();
您首先需要获取 MetadataSources,从中获取 MetadataBuilder,然后使用它来添加您的类。通过 是可以的hibernate.cfg.xml
,但是也有点麻烦。
但是注册之后,你可以这样写:
@Entity
@Table(name="user")
class User
{
@Id
@Column(name="id")
public Integer id;
@Column(name="join_time")
@Type(type = "LocalTimeString")
public java.time.LocalTime time;
}
GO TO FULL VERSION