7.1 अपना खुद का टाइप कन्वर्टर बनाना

कभी-कभी ऐसी स्थितियाँ उत्पन्न होती हैं जब आप किसी तालिका के एक स्तंभ में एक काफी जटिल डेटा प्रकार को संग्रहीत करना चाहते हैं। यदि हाइबरनेट जानता है कि इसे स्ट्रिंग (और पीछे) में कैसे परिवर्तित किया जाए, तो सब कुछ ठीक है। यदि नहीं, तो आपको अपना खुद का डेटा कन्वर्टर लिखना होगा।

मान लीजिए कि कोई उपयोगकर्ता के जन्म के वर्ष को डेटाबेस में संग्रहीत करने का निर्णय लेता है 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 मानों के साथ CHARs के रूप में, और पसंद किया जा सकता है।

7.2 अपना स्वयं का डेटा प्रकार बनाना

तालिका को उन प्रकारों की सूची के साथ याद रखें जिन्हें हाइबरनेट के लिए जाना जाता है? मैं एनोटेशन के साथ निर्दिष्ट प्रकारों के बारे में बात कर रहा हूं @Type। आप अपना स्वयं का डेटा प्रकार लिख सकते हैं, जिसका उपयोग हाइबरनेट में अन्य अंतर्निर्मित प्रकारों के समान ही किया जा सकता है।

उदाहरण के लिए, हम चाहते हैं कि लोकलटाइम प्रकार हो, जो डेटाबेस में TIME के ​​रूप में नहीं, बल्कि VARCHAR के रूप में संग्रहीत किया जाएगा। और, उदाहरण के लिए, हमारे पास ऐसे डेटाबेस तक पहुंच है, और हमें इसके कॉलम में डेटा प्रकार बदलने की अनुमति नहीं है। तब हम अपना स्वयं का हाइबरनेट प्रकार लिख सकते हैं। आइए इसे लोकलटाइमस्ट्रिंग कहते हैं।

पहले हमें एक छोटे वर्ग की आवश्यकता है जो हमारे नए प्रकार का वर्णन करे:

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 है जिसमें एक मान होता है। ऐसे एकल एनैम्स का सेट हाइबरनेट के लिए जाने जाने वाले सभी प्रकार हैं।

हमें एक वर्ग की भी आवश्यकता है - कनवर्टर का एक एनालॉग, जिसमें दो विधियाँ होंगी - wrap()और unwrap()लोकलटाइम प्रकार के मूल्यों को स्ट्रिंग में परिवर्तित करने के लिए।

तरीकों को लागू किए बिना यह कैसा दिखेगा:

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 अपने प्रकार को पंजीकृत करना

आप हाइबरनेट कॉन्फ़िगरेशन के दौरान अपना डेटा प्रकार भी पंजीकृत कर सकते हैं। यह थोड़ा गैर-तुच्छ है।


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();

आपको पहले मेटाडेटा स्रोत प्राप्त करने की आवश्यकता होगी, इससे मेटाडेटाबिल्डर प्राप्त करें, और अपनी कक्षा जोड़ने के लिए इसका उपयोग करें। यह के माध्यम से संभव है 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;
}