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;
}

บ่อยครั้งที่ต้องใช้ตัวแปลงหากคุณไม่ได้ออกแบบฐานข้อมูล ข้อมูลอาจอยู่ในรูปแบบ "แปลก ๆ " สามารถจัดเก็บวันที่เป็นสตริง บูลีนเป็น CHAR ที่มีค่า Y และ N และอื่นๆ

7.2 การสร้างประเภทข้อมูลของเราเอง

จำตารางที่มีรายการประเภทที่ Hibernate รู้จักได้หรือไม่ ฉันกำลังพูดถึงประเภทที่ระบุพร้อมกับคำอธิบาย@Typeประกอบ คุณสามารถเขียนประเภทข้อมูลของคุณเอง ซึ่งสามารถใช้ในลักษณะเดียวกับประเภทในตัวอื่น ๆ ในไฮเบอร์เนต

ตัวอย่างเช่น เราต้องการให้มีประเภท 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";
    }
}

เป็นประเภท Enum ซึ่งประกอบด้วยค่าเดียว ชุดของการเคลือบเดี่ยวดังกล่าวเป็นประเภททั้งหมดที่ Hibernate รู้จัก

เราต้องการคลาส - อะนาล็อกของตัวแปลงซึ่งจะมีสองวิธี - wrap()และunwrap()สำหรับการแปลงค่าของประเภท LocalTime เป็นสตริง

นี่คือลักษณะที่จะไม่ใช้วิธีการ:

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;
}