7.1 創建自己的類型轉換器

有時,當您想在表的一列中存儲相當複雜的數據類型時,會出現這種情況。如果 Hibernate 知道如何將它轉換為字符串(並返回),那麼一切都很好。如果沒有,那麼您將不得不編寫自己的數據轉換器。

假設某人決定將用戶的出生年份存儲在數據庫中YY.MM.DD,例如: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;
}