Spring和Hibernate动态建表及动态加载映射文件(无需Session factory Rebuild)

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


Java代码  收藏代码

Java代码   收藏代码

    //add by csx 
    public void addNewConfig(Configuration cfg){ 
        log.info("add NewConfig....."); 
         
        Mapping mapping=this.configuration.getMapping(); 
        this.filters.putAll( cfg.getFilterDefinitions() ); 
        //Generators: 
        Iterator classes = cfg.getClassMappings(); 
        while ( classes.hasNext() ) { 
            PersistentClass model = (PersistentClass) classes.next(); 
             
            if ( !model.isInherited() ) { 
                IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator( 
                        settings.getDialect(), 
                        settings.getDefaultCatalogName(), 
                        settings.getDefaultSchemaName(), 
                        (RootClass) model 
                    ); 
                identifierGenerators.put( model.getEntityName(), generator ); 
            } 
        } 
     
        /////////////////////////////////////////////////////////////////////// 
        // Prepare persisters and link them up with their cache 
        // region/access-strategy 
     
        String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + "."; 
         
        Map entityAccessStrategies = new HashMap(); 
        Map tmpEntityPersisters=new HashMap(); 
        Map tmpClassMetadata=new HashMap(); 
         
         
        this.configuration.getClassMap().putAll(cfg.getClassMap()); 
        classes = cfg.getClassMappings(); 
     
        while ( classes.hasNext() ) { 
             
            PersistentClass model = (PersistentClass) classes.next(); 
             
            model.prepareTemporaryTables( mapping, settings.getDialect() ); 
            String cacheRegionName = cacheRegionPrefix + model.getRootClass().getCacheRegionName(); 
             
            // cache region is defined by the root-class in the hierarchy... 
            EntityRegionAccessStrategy accessStrategy = ( EntityRegionAccessStrategy ) entityAccessStrategies.get( cacheRegionName ); 
            if ( accessStrategy == null && settings.isSecondLevelCacheEnabled() ) { 
                AccessType accessType = AccessType.parse( model.getCacheConcurrencyStrategy() ); 
                if ( accessType != null ) { 
                     
                    log.trace( "Building cache for entity data [" + model.getEntityName() + "]" ); 
                    EntityRegion entityRegion = settings.getRegionFactory().buildEntityRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) ); 
                    accessStrategy = entityRegion.buildAccessStrategy( accessType ); 
                    entityAccessStrategies.put( cacheRegionName, accessStrategy ); 
                    allCacheRegions.put( cacheRegionName, entityRegion ); 
                } 
            } 
            EntityPersister cp = PersisterFactory.createClassPersister( model, accessStrategy, this, cfg.getMapping() ); 
            tmpEntityPersisters.put( model.getEntityName(), cp ); 
            tmpClassMetadata.put( model.getEntityName(), cp.getClassMetadata() ); 
             
        } 
         
        //Named Queries: 
        namedQueries.putAll(cfg.getNamedQueries()); 
        namedSqlQueries.putAll( cfg.getNamedSQLQueries() ); 
        sqlResultSetMappings.putAll(cfg.getSqlResultSetMappings()); 
        imports.putAll(cfg.getImports()); 
     
        entityPersisters.putAll(tmpEntityPersisters); 
     
        classMetadata.putAll(tmpClassMetadata); 
         
        Map tmpEntityToCollectionRoleMap = new HashMap(); 
        Map tempCollectionPersisters=new HashMap(); 
         
        this.configuration.getCollectionMap().putAll(cfg.getCollectionMap()); 
        Iterator collections = cfg.getCollectionMappings(); 
     
        while ( collections.hasNext() ) { 
            Collection model = (Collection) collections.next(); 
            final String cacheRegionName = cacheRegionPrefix + model.getCacheRegionName(); 
            final AccessType accessType = AccessType.parse( model.getCacheConcurrencyStrategy() ); 
            CollectionRegionAccessStrategy accessStrategy = null; 
            if ( accessType != null && settings.isSecondLevelCacheEnabled() ) { 
                log.trace( "Building cache for collection data [" + model.getRole() + "]" ); 
                CollectionRegion collectionRegion = settings.getRegionFactory().buildCollectionRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) ); 
                accessStrategy = collectionRegion.buildAccessStrategy( accessType ); 
                entityAccessStrategies.put( cacheRegionName, accessStrategy ); 
                allCacheRegions.put( cacheRegionName, collectionRegion ); 
            } 
            CollectionPersister persister = PersisterFactory.createCollectionPersister(this.getConfiguration(), model, accessStrategy, this) ; 
            tempCollectionPersisters.put( model.getRole(), persister.getCollectionMetadata() ); 
            Type indexType = persister.getIndexType(); 
            if ( indexType != null && indexType.isAssociationType() && !indexType.isAnyType() ) { 
                String entityName = ( ( AssociationType ) indexType ).getAssociatedEntityName( this ); 
                Set roles = ( Set ) tmpEntityToCollectionRoleMap.get( entityName ); 
                if ( roles == null ) { 
                    roles = new HashSet(); 
                    tmpEntityToCollectionRoleMap.put( entityName, roles ); 
                } 
                roles.add( persister.getRole() ); 
            } 
            Type elementType = persister.getElementType(); 
            if ( elementType.isAssociationType() && !elementType.isAnyType() ) { 
                String entityName = ( ( AssociationType ) elementType ).getAssociatedEntityName( this ); 
                Set roles = ( Set ) tmpEntityToCollectionRoleMap.get( entityName ); 
                if ( roles == null ) { 
                    roles = new HashSet(); 
                    tmpEntityToCollectionRoleMap.put( entityName, roles ); 
                } 
                roles.add( persister.getRole() ); 
            } 
             
        } 
        //加入新的 
        collectionPersisters.putAll(tempCollectionPersisters); 
     
        Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator(); 
        while ( itr.hasNext() ) { 
            final Map.Entry entry = ( Map.Entry ) itr.next(); 
            entry.setValue( Collections.unmodifiableSet( ( Set ) entry.getValue() ) ); 
        } 
         
        collectionRolesByEntityParticipant.putAll( tmpEntityToCollectionRoleMap); 
     
        // after *all* persisters and named queries are registered 
        Iterator iter = tmpEntityPersisters.values().iterator(); 
        while ( iter.hasNext() ) { 
            ( (EntityPersister) iter.next() ).postInstantiate(); 
        } 
        iter = tempCollectionPersisters.values().iterator(); 
        while ( iter.hasNext() ) { 
            ( (CollectionPersister) iter.next() ).postInstantiate(); 
        } 
         
        queryPlanCache=new QueryPlanCache(this); 
         
         
    } 



我们动态加入实体,动态可进行查询,Hibernate提供几种实体的查询策略,其中一个是我们常用的pojo,若采用该方法,我们得加上hbm与 class类或仅是含注解的class至hibernate的SessionFactory,这种方案并且没有问题,但会遇到当我们修改表单字段,重新生 成对应的实体时,我们就会遇到原有的实体class不能在ClassLoader里卸载。使用的还是旧的Class,达不到动态查询的效果。若使用动态的 ClassLoader,代码将变得很复杂。(尝试过,代码相对比较繁杂)



        还好Hibernate提供了另一种实体查询策略,基于Map的动态实体。基于这种方式,我们配置一个一对多的表,其示例代码如下:



MainEntity.hbm.xml
Java代码   收藏代码

    <?xml version="1.0"?> 
    <!DOCTYPE hibernate-mapping PUBLIC 
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
            "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
    <hibernate-mapping> 
        <class table="main_entity" entity-name="MainEntity"> 
            <id name="mainId" column="mainId" type="java.lang.Long"> 
                <generator class="native"/> 
            </id> 
            <property name="itemSubject" type="java.lang.String" length="128"/> 
            <property name="itemDescp" type="java.lang.String" /> 
            <property name="createtime" type="java.util.Date" /> 
            <bag name="subEntitys" 
                 table="sub_entity" 
                 lazy="false" 
                 inverse="true" 
                 cascade="save-update,delete-orphan" 
            > 
                <key> 
                    <column name="mainId"/> 
                </key> 
                <one-to-many entity-name="SubEntity"/> 
            </bag> 
        </class> 
    </hibernate-mapping> 

SubEntity.hbm.xml




Java代码   收藏代码

    <?xml version="1.0"?> 
    <!DOCTYPE hibernate-mapping PUBLIC 
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
            "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
    <hibernate-mapping> 
        <class table="sub_entity" entity-name="SubEntity"> 
            <id name="subId" column="subId" type="java.lang.Long"> 
                <generator class="native"/> 
            </id> 
            <property name="subject" type="java.lang.String" length="128"/> 
            <property name="createtime" type="java.util.Date"/> 
            <many-to-one name="mainEntity" entity-name="MainEntity" not-null="false" fetch="select"> 
                <column name="mainId"></column> 
            </many-to-one> 
        </class> 
    </hibernate-mapping> 



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




Java代码   收藏代码

    public static void main(String[]args){ 
            ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("classpath:app-context.xml"); 
     
            MyAnnotationSessionFactoryBean sessionFactoryBean=(MyAnnotationSessionFactoryBean)ctx.getBean("&sessionFactory"); 
             
            SessionFactoryImpl sessionFactoryImpl=(SessionFactoryImpl)sessionFactoryBean.getObject(); 
         
            Configuration cfg=new Configuration();   
            //cfg.configure(); 
             
            cfg.addFile("D:/download/eclipse/workspace2/SpringHibernate/src/com/hotent/entity/MainEntity.hbm.xml"); 
            cfg.addFile("D:/download/eclipse/workspace2/SpringHibernate/src/com/hotent/entity/SubEntity.hbm.xml"); 
            cfg.doComplie(); 
     
            sessionFactoryImpl.addNewConfig(cfg); 
     
            MainEntityDao dao=(MainEntityDao)ctx.getBean("mainEntityDao"); 
     
            List list=dao.query(); 
     
            for(int i=0;i<list.size();i++){ 
                Map map=(Map)list.get(i); 
                Iterator it=map.keySet().iterator(); 
                while(it.hasNext()){ 
                    //String key=(String)it.next(); 
                    Object key=it.next(); 
                    Object val=map.get(key); 
                    System.out.println("--------------->key:"+key ); 
                } 
            } 
     
            Configuration cfg2=new Configuration(); 
            cfg2.addFile("D:/dev/product/SpringHibernate/src/com/hotent/entity/MainEntity2.hbm.xml"); 
            cfg2.addFile("D:/dev/product/SpringHibernate/src/com/hotent/entity/SubEntity2.hbm.xml"); 
            cfg2.doComplie(); 
            sessionFactoryImpl.addNewConfig(cfg2); 
             
            List list2=dao.query(); 
             
            for(int i=0;i<list2.size();i++){ 
                Map map=(Map)list2.get(i); 
                Iterator it=map.keySet().iterator(); 
                while(it.hasNext()){ 
                    //String key=(String)it.next(); 
                    Object key=it.next(); 
                    Object val=map.get(key); 
                    System.out.println("key:"+key ); 
                } 
            } 
             
        } 


Java代码   收藏代码

    MainEntity2.hbm.xml与SubEntity2.hbm.xml文件相对原来的文件增加了一些列或删除了一些列 



执行后,可以实时看到不同的结果,并且不会影响现有的dao。

猜你喜欢

转载自hanks-lin.iteye.com/blog/1611766