Spring与Hibernate动态建表及动态加载映射文件(无需SessionFactory Rebuild)

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类中加入以下方法,(有一些变量值不能修改的,请改为可修改)

 


spring 镜像_classloader


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


spring 镜像_classloader



    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

     

     


    spring 镜像_classloader



      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>


      为两实体插入数据后,可动态测试如下:

       

       


      spring 镜像_classloader


      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。