简体   繁体   中英

How to persist dynamic extension property in Hibernate?

I am facing a complex requirement, puzzling me a lot. Help...

Hibernate:4.3.6 MySql 5.6.21

For support dynamic extension on standard product, such as: Standard POJO:

 package com.inspur.gsp; import java.util.HashMap; import java.util.Map; public class Person { private String id; private String age; private Map<CustomPK, Person_T> i18nProperty = new HashMap<CustomPK, Person_T>(); private Map<String, HashMap<String, Object>> extProperty = new HashMap<String, HashMap<String, Object>>(); public Map<CustomPK, Person_T> getI18nProperty() { return i18nProperty; } public void setI18nProperity(Map<CustomPK, Person_T> i18nProperty) { this.i18nProperty = i18nProperty; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public Map<String, HashMap<String, Object>> getExtProperty() { return extProperty; } public void setExtProperty(Map<String, HashMap<String, Object>> extProperty) { this.extProperty = extProperty; } } 

So, ExtProperity's type is Map<String, Map<String, Object>> , it will be used for storage extension property. Then we need to persist it to database.

Here is my hbm.xml:

 <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated Nov 3, 2014 11:48:31 AM by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.inspur.gsp.Person" table="GSPPerson"> <id name="id" type="java.lang.String"> <column name="ID" length="36" /> <generator class="assigned" /> </id> <property name="age" type="java.lang.String"> <column name="age" length="128" /> </property> <map name="i18nProperty" table="GSPPerson_T"> <key column="ID"></key> <composite-map-key class="com.inspur.gsp.CustomPK"> <key-property name="culture"></key-property> </composite-map-key> <composite-element class="com.inspur.gsp.Person_T"> <property name="description" column="description"></property> <property name="comments" column="comments"></property> </composite-element> </map> <map name="extProperty" table="GSPPerson_Ext"> <key column="ID"></key> <map-key type="string" column="ID" /> <composite-element class="java.util.HashMap"> <property name="ext1" column="ext1"></property> <property name="ext2" column="ext2"></property> </composite-element> </map> </class> </hibernate-mapping> 

Test code:

 SessionFactory sf = createSessionFactory(); Person person = new Person(); person.setId("1"); person.setAge("30"); HashMap<CustomPK, Person_T> i18nProperity = new HashMap<CustomPK, Person_T>(); Person_T t1 = new Person_T(); t1.setComments("abc"); t1.setDescription("description"); Person_T t2 = new Person_T(); t2.setComments("efg"); t2.setDescription("ooooooo"); i18nProperity.put(new CustomPK("1", "cn"), t1); i18nProperity.put(new CustomPK("1", "en"), t2); person.setI18nProperity(i18nProperity); HashMap<String, HashMap<String,Object>> extProperty = new HashMap<String, HashMap<String,Object>>(); HashMap<String,Object> e1 = new HashMap<String,Object>(); e1.put("ext1", 1); e1.put("ext2", "c001"); extProperty.put("1", e1);//1 is person's id ,e1 is extension property. person.setExtProperty(extProperty); Session session = sf.getCurrentSession(); Transaction ts = session.beginTransaction(); session.save(person); Person person2 = (Person) session.get(Person.class, "1"); ts.commit(); 

The configuraion in hbm.xml cannot work. Please help me. Thanks.

 INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5InnoDBDialect Exception in thread "main" org.hibernate.PropertyNotFoundException: field [ext1] not found on java.util.HashMap at org.hibernate.property.DirectPropertyAccessor.getField(DirectPropertyAccessor.java:166) at org.hibernate.property.DirectPropertyAccessor.getField(DirectPropertyAccessor.java:173) at org.hibernate.property.DirectPropertyAccessor.getField(DirectPropertyAccessor.java:158) at org.hibernate.property.DirectPropertyAccessor.getGetter(DirectPropertyAccessor.java:181) at org.hibernate.internal.util.ReflectHelper.getter(ReflectHelper.java:254) at org.hibernate.internal.util.ReflectHelper.reflectedPropertyClass(ReflectHelper.java:230) at org.hibernate.mapping.SimpleValue.setTypeUsingReflection(SimpleValue.java:362) at org.hibernate.cfg.HbmBinder.createProperty(HbmBinder.java:2350) at org.hibernate.cfg.HbmBinder.bindComponent(HbmBinder.java:2037) at org.hibernate.cfg.HbmBinder.bindComposite(HbmBinder.java:1836) at org.hibernate.cfg.HbmBinder.bindCollectionSecondPass(HbmBinder.java:2634) at org.hibernate.cfg.HbmBinder.bindMapSecondPass(HbmBinder.java:2468) at org.hibernate.cfg.HbmBinder$MapSecondPass.secondPass(HbmBinder.java:2843) at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:70) at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1695) at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1424) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844) at I18nTest.createSessionFactory(I18nTest.java:84) at I18nTest.main(I18nTest.java:37) 

Table structure of GSPPerson is:

ID      Age    
1       30     

Table structure of GSPPerson_ext is:

ID   ext1    ext2
1    1       c001

HashMap<String, HashMap<String,Object>> extProperty = new HashMap<String, HashMap<String,Object>>();
        HashMap<String,Object> e1 = new HashMap<String,Object>();
        e1.put("ext1", 1);
        e1.put("ext2", "c001");

        extProperty.put("1", e1);//1 is person's,e1 is extension property.
        person.setExtProperty(extProperty);

Ext1 and Ext2 are extension columns, so we defined a Hashmap<String,HashMap<String,Object> to storage the extension columns. We hope persist the Hashmap to extension columns.

You're defining a property ext1 on a HashMap , which it doesn't have, so Hibernate will look for those properties. What does ext1 mean anyways?

Additionally, you're putting Object into the map, which Hibernate would not be able to map.

I'm not sure about what extensions are in your case or what the key of that nested map does represent, but I'd probably model extensions as separate entities. Something like that:

class Extension {
  String key;
  Type type; //Type could be an Enum, String, a Number etc. whatever fits your needs
  String value; //use a text representation of the values and convert according to the type
}

And in your Person class:

Map<String, Extension> extensions = ...;

As you can see I'd map the extension value to String and not Object in order to allow Hibernate to map that value correctly. Most types (at least primitives) can be converted to and from strings anyways so that would not be a problem. If the values get more complex you'd either need an appropriate string mapping (eg JSON or XML), serialize the values to byte arrays or make the values custom entities.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM