Spring与Hibernate动态建表及动态加载映射文件(无需SessionFactory Rebuild)
- 博客分类:
- Hibernate
Hibernate Spring DAO XML Eclipse
Spring与Hibernate动态建表及动态加载映射文件(无需SessionFactory Rebuild)
J.Office2有一功能是工作流支持动态表单设计,设计后可以动态生成数据库表,并且支持实时查询(单表及多表均可)。
由于J.Office2版本中采用了Hibernate作为底层的ORM框架,结合Spring框架,Spring容器启动后,SessionFactory就会被注入到各个业务的Dao层中去。
动态建表功能比较容易实现,我们可以new一个SessionFactory,然后把它的配置属性hibernate.hbm2ddl.auto改为update或create,就可以达到动态修改表结构的效果。
但若要加入新的hbm或class,需要重新调用SessionFactoryBean来获取一个全新的SessionFactory,这种方案试过了,效果并不理想。重新加载,会导致大量的hbm或class文件重新加载,实在有点慢。并且严重影响现在注入SessionFactory的Dao。若Dao采用动态构建SessionFactory,性能又是一问题。而Hibernate没有提供SessionFactory动态加入hbm或Class文件。所以实在无计可施。
所以最终还是回到如何扩展Hibernate的SessionFactory类中去了,这想法已经有不少开发人员尝试过,JE也有一帖子专门讨论这个。不过仅是一Demo,不完善。我们提供了两个扩展的类(修改Hibernate中的两类,使其支持动态加入配置文件,并且能实时查询。
我们仅需要修改两个类,一个是Configuration,在其里面加一方法,如下:
public void doComplie(){
secondPassCompile();
}
修改
在SessonFactoryImpl类中加入以下方法,(有一些变量值不能修改的,请改为可修改)
1. //add by csx
2. public void
3. "add NewConfig.....");
4.
5. this.configuration.getMapping();
6. this.filters.putAll( cfg.getFilterDefinitions() );
7. //Generators:
8. Iterator classes = cfg.getClassMappings();
9. while
10. PersistentClass model = (PersistentClass) classes.next();
11.
12. if
13. IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(
14. settings.getDialect(),
15. settings.getDefaultCatalogName(),
16. settings.getDefaultSchemaName(),
17. (RootClass) model
18. );
19. identifierGenerators.put( model.getEntityName(), generator );
20. }
21. }
22.
23. ///
24. // Prepare persisters and link them up with their cache
25. // region/access-strategy
26.
27. null ? "" : settings.getCacheRegionPrefix() + ".";
28.
29. new
30. new
31. new
32.
33.
34. this.configuration.getClassMap().putAll(cfg.getClassMap());
35. classes = cfg.getClassMappings();
36.
37. while
38.
39. PersistentClass model = (PersistentClass) classes.next();
40.
41. model.prepareTemporaryTables( mapping, settings.getDialect() );
42. String cacheRegionName = cacheRegionPrefix + model.getRootClass().getCacheRegionName();
43.
44. // cache region is defined by the root-class in the hierarchy...
45. EntityRegionAccessStrategy accessStrategy = ( EntityRegionAccessStrategy ) entityAccessStrategies.get( cacheRegionName );
46. if ( accessStrategy == null
47. AccessType accessType = AccessType.parse( model.getCacheConcurrencyStrategy() );
48. if ( accessType != null
49.
50. "Building cache for entity data [" + model.getEntityName() + "]"
51. EntityRegion entityRegion = settings.getRegionFactory().buildEntityRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
52. accessStrategy = entityRegion.buildAccessStrategy( accessType );
53. entityAccessStrategies.put( cacheRegionName, accessStrategy );
54. allCacheRegions.put( cacheRegionName, entityRegion );
55. }
56. }
57. this, cfg.getMapping() );
58. tmpEntityPersisters.put( model.getEntityName(), cp );
59. tmpClassMetadata.put( model.getEntityName(), cp.getClassMetadata() );
60.
61. }
62.
63. //Named Queries:
64. namedQueries.putAll(cfg.getNamedQueries());
65. namedSqlQueries.putAll( cfg.getNamedSQLQueries() );
66. sqlResultSetMappings.putAll(cfg.getSqlResultSetMappings());
67. imports.putAll(cfg.getImports());
68.
69. entityPersisters.putAll(tmpEntityPersisters);
70.
71. classMetadata.putAll(tmpClassMetadata);
72.
73. new
74. new
75.
76. this.configuration.getCollectionMap().putAll(cfg.getCollectionMap());
77. Iterator collections = cfg.getCollectionMappings();
78.
79. while
80. Collection model = (Collection) collections.next();
81. final
82. final
83. null;
84. if ( accessType != null
85. "Building cache for collection data [" + model.getRole() + "]"
86. CollectionRegion collectionRegion = settings.getRegionFactory().buildCollectionRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
87. accessStrategy = collectionRegion.buildAccessStrategy( accessType );
88. entityAccessStrategies.put( cacheRegionName, accessStrategy );
89. allCacheRegions.put( cacheRegionName, collectionRegion );
90. }
91. this.getConfiguration(), model, accessStrategy, this) ;
92. tempCollectionPersisters.put( model.getRole(), persister.getCollectionMetadata() );
93. Type indexType = persister.getIndexType();
94. if ( indexType != null
95. this
96. Set roles = ( Set ) tmpEntityToCollectionRoleMap.get( entityName );
97. if ( roles == null
98. new
99. tmpEntityToCollectionRoleMap.put( entityName, roles );
100. }
101. roles.add( persister.getRole() );
102. }
103. Type elementType = persister.getElementType();
104. if
105. this
106. Set roles = ( Set ) tmpEntityToCollectionRoleMap.get( entityName );
107. if ( roles == null
108. new
109. tmpEntityToCollectionRoleMap.put( entityName, roles );
110. }
111. roles.add( persister.getRole() );
112. }
113.
114. }
115. //加入新的
116. collectionPersisters.putAll(tempCollectionPersisters);
117.
118. Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator();
119. while
120. final
121. entry.setValue( Collections.unmodifiableSet( ( Set ) entry.getValue() ) );
122. }
123.
124. collectionRolesByEntityParticipant.putAll( tmpEntityToCollectionRoleMap);
125.
126. // after *all* persisters and named queries are registered
127. Iterator iter = tmpEntityPersisters.values().iterator();
128. while
129. ( (EntityPersister) iter.next() ).postInstantiate();
130. }
131. iter = tempCollectionPersisters.values().iterator();
132. while
133. ( (CollectionPersister) iter.next() ).postInstantiate();
134. }
135.
136. new QueryPlanCache(this);
137.
138.
139. }
我们动态加入实体,动态可进行查询,Hibernate提供几种实体的查询策略,其中一个是我们常用的pojo,若采用该方法,我们得加上hbm与class类或仅是含注解的class至hibernate的SessionFactory,这种方案并且没有问题,但会遇到当我们修改表单字段,重新生成对应的实体时,我们就会遇到原有的实体class不能在ClassLoader里卸载。使用的还是旧的Class,达不到动态查询的效果。若使用动态的ClassLoader,代码将变得很复杂。(尝试过,代码相对比较繁杂)
还好Hibernate提供了另一种实体查询策略,基于Map的动态实体。基于这种方式,我们配置一个一对多的表,其示例代码如下:
MainEntity.hbm.xml
1. <?xml version="1.0"?>
2. <!DOCTYPE hibernate-mapping PUBLIC
3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
5. <hibernate-mapping>
6. class table="main_entity" entity-name="MainEntity">
7. "mainId" column="mainId" type="java.lang.Long">
8. class="native"/>
9. </id>
10. "itemSubject" type="java.lang.String" length="128"/>
11. "itemDescp" type="java.lang.String"
12. "createtime" type="java.util.Date"
13. "subEntitys"
14. "sub_entity"
15. "false"
16. "true"
17. "save-update,delete-orphan"
18. >
19. <key>
20. "mainId"/>
21. </key>
22. "SubEntity"/>
23. </bag>
24. class>
25. </hibernate-mapping>
SubEntity.hbm.xml
1. <?xml version="1.0"?>
2. <!DOCTYPE hibernate-mapping PUBLIC
3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
5. <hibernate-mapping>
6. class table="sub_entity" entity-name="SubEntity">
7. "subId" column="subId" type="java.lang.Long">
8. class="native"/>
9. </id>
10. "subject" type="java.lang.String" length="128"/>
11. "createtime" type="java.util.Date"/>
12. "mainEntity" entity-name="MainEntity" not-null="false" fetch="select">
13. "mainId"></column>
14. </many-to-one>
15. class>
16. </hibernate-mapping>
为两实体插入数据后,可动态测试如下:
1. public static void
2. new ClassPathXmlApplicationContext("classpath:app-context.xml");
3.
4. "&sessionFactory");
5.
6. SessionFactoryImpl sessionFactoryImpl=(SessionFactoryImpl)sessionFactoryBean.getObject();
7.
8. new
9. //cfg.configure();
10.
11. "D:/download/eclipse/workspace2/SpringHibernate/src/com/hotent/entity/MainEntity.hbm.xml");
12. "D:/download/eclipse/workspace2/SpringHibernate/src/com/hotent/entity/SubEntity.hbm.xml");
13. cfg.doComplie();
14.
15. sessionFactoryImpl.addNewConfig(cfg);
16.
17. "mainEntityDao");
18.
19. List list=dao.query();
20.
21. for(int i=0;i<list.size();i++){
22. Map map=(Map)list.get(i);
23. Iterator it=map.keySet().iterator();
24. while(it.hasNext()){
25. //String key=(String)it.next();
26. Object key=it.next();
27. Object val=map.get(key);
28. "--------------->key:"+key );
29. }
30. }
31.
32. new
33. "D:/dev/product/SpringHibernate/src/com/hotent/entity/MainEntity2.hbm.xml");
34. "D:/dev/product/SpringHibernate/src/com/hotent/entity/SubEntity2.hbm.xml");
35. cfg2.doComplie();
36. sessionFactoryImpl.addNewConfig(cfg2);
37.
38. List list2=dao.query();
39.
40. for(int i=0;i<list2.size();i++){
41. Map map=(Map)list2.get(i);
42. Iterator it=map.keySet().iterator();
43. while(it.hasNext()){
44. //String key=(String)it.next();
45. Object key=it.next();
46. Object val=map.get(key);
47. "key:"+key );
48. }
49. }
50.
51. }
1. MainEntity2.hbm.xml与SubEntity2.hbm.xml文件相对原来的文件增加了一些列或删除了一些列
执行后,可以实时看到不同的结果,并且不会影响现有的dao。