文档章节

hibernate动态创建表, 动态加载映射

juqkai
 juqkai
发布于 2013/08/05 15:57
字数 1470
阅读 1277
收藏 2

参考:

1. http://www.iteye.com/topic/1112024    (Hibernate的动态模型)

2. http://man1900.iteye.com/blog/897933  (

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

)(注: 不知道是什么版本的hibernate, 反正4不能用)

如果大家有更好的实现方式, 希望能指点.谢谢

正文:

项目中有个需求, 要动态添加表结构, 同时要对其进行增删改查等操作, 而且还会有级联关系.



很多系统中都有这样的需求, 大部分都是使用JDBC自己实现. 最初也想这么做, 但是发现工作量不是一点的. 后来研究了一下hibernate发现如果对hibernate做一些扩展, 还是可以实现的.


涉及的内容:


1. Hibernate的动态模型

原理:

hibernate首先会通过Configuration读取配置文件, 然后进行解析, 最后生成SessionFactory, 当我们操作数据库时通过SessionFactory生成一个Session, 然后再进行对应的操作. 而要实现动态添加表(使用动态模型), 就需要解析XML, 然后将解析出来的内容整合到Session中, 保证在进行增删改查的时候能在Session中取到想要的东西. 因此要实现动态模型就有两个步骤.

1. 解析XML

2. 整合

解析的话, 直接使用

Configuration cfg = new Configuration();
 cfg.addFile(new File("Code.hbm.xml"));
 cfg.buildMappings();

就可以解析出来了. 

但是整合就麻烦了, 因为没有入口, hibernate没有提供现成的接口, 要不然也不会有这篇文章了. 通过查看SessionFactoryImpl源码可以发现, 所有解析configuration都是在构造函数中实现的, 那么, 我可完全可以复制一份构造函数, 然后做一定的改造, 使该方法可以合并Configuration内的东西, 当然可能会遗漏很多东西, 但是我们需求也不多.


在自己工程里创建包org.hibernate.internal, 把hibernate中的SessionFactoryImpl复制到这个包下, 然后对它进行修改, 之所以这样, 是为了让JDK在加载CLASS时替换掉hibernate自带的SessionFatoryImpl, 有些内容没法实现, 如Collections.unmodifiableMap处理过的MAP

代码在最下面, 添加这个方法后, 还要把构造函数里面几处Collections.unmodifiableMap去掉...


DEMO的话大家自己用hibernate里面的DEMO改一下就OK了

注:本方法基于hibernate-core4.2.0.Final

/**
     * 合并配置
     * @param cfg
     * @throws org.hibernate.HibernateException
     */
//    @SuppressWarnings( {"unchecked", "ThrowableResultOfMethodCallIgnored"})
    public void mergeConfiguration(final Configuration cfg) throws HibernateException {
        LOG.debug( "Building session factory" );

        Mapping mapping = cfg.buildMapping();

        this.properties.putAll( cfg.getProperties() );
        this.filters.putAll( cfg.getFilterDefinitions() );

        final RegionFactory regionFactory = cacheAccess.getRegionFactory();

        LOG.debugf( "Session factory constructed with filter configurations : %s", filters );
        LOG.debugf( "Instantiating session factory with properties: %s", properties );

        //Generators:

        Iterator classes = cfg.getClassMappings();
        while ( classes.hasNext() ) {
            PersistentClass model = (PersistentClass) classes.next();
            if ( !model.isInherited() ) {
                IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(
                        cfg.getIdentifierGeneratorFactory(),
                        getDialect(),
                        settings.getDefaultCatalogName(),
                        settings.getDefaultSchemaName(),
                        (RootClass) model
                );
                identifierGenerators.put( model.getEntityName(), generator );
            }
        }


        ///////////////////////////////////////////////////////////////////////
        // Prepare persisters and link them up with their cache
        // region/access-strategy

        final String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + ".";

        final PersisterFactory persisterFactory = serviceRegistry.getService( PersisterFactory.class );

        Map entityAccessStrategies = new HashMap();
        Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>();
        classes = cfg.getClassMappings();
        while ( classes.hasNext() ) {
            final PersistentClass model = (PersistentClass) classes.next();
            model.prepareTemporaryTables( mapping, getDialect() );
            final 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() ) {
                final AccessType accessType = AccessType.fromExternalName( model.getCacheConcurrencyStrategy() );
                if ( accessType != null ) {
                    LOG.tracef( "Building shared cache region for entity data [%s]", model.getEntityName() );
//                    EntityRegion entityRegion = regionFactory.buildEntityRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
                    EntityRegion entityRegion = regionFactory.buildEntityRegion(cacheRegionName, properties, CacheDataDescriptionImpl.decode(model));

                    accessStrategy = entityRegion.buildAccessStrategy( accessType );
                    entityAccessStrategies.put( cacheRegionName, accessStrategy );
                    cacheAccess.addCacheRegion( cacheRegionName, entityRegion );
                }
            }

            NaturalIdRegionAccessStrategy naturalIdAccessStrategy = null;
            if ( model.hasNaturalId() && model.getNaturalIdCacheRegionName() != null ) {
                final String naturalIdCacheRegionName = cacheRegionPrefix + model.getNaturalIdCacheRegionName();
                naturalIdAccessStrategy = ( NaturalIdRegionAccessStrategy ) entityAccessStrategies.get( naturalIdCacheRegionName );

                if ( naturalIdAccessStrategy == null && settings.isSecondLevelCacheEnabled() ) {
                    final CacheDataDescriptionImpl cacheDataDescription = CacheDataDescriptionImpl.decode( model );

                    NaturalIdRegion naturalIdRegion = null;
                    try {
                        naturalIdRegion = regionFactory.buildNaturalIdRegion(naturalIdCacheRegionName, properties,
                                cacheDataDescription);
                    }
                    catch ( UnsupportedOperationException e ) {
                        LOG.warnf(
                                "Shared cache region factory [%s] does not support natural id caching; " +
                                        "shared NaturalId caching will be disabled for not be enabled for %s",
                                regionFactory.getClass().getName(),
                                model.getEntityName()
                        );
                    }

                    if (naturalIdRegion != null) {
                        naturalIdAccessStrategy = naturalIdRegion.buildAccessStrategy( regionFactory.getDefaultAccessType() );
                        entityAccessStrategies.put( naturalIdCacheRegionName, naturalIdAccessStrategy );
                        cacheAccess.addCacheRegion(  naturalIdCacheRegionName, naturalIdRegion );
                    }
                }
            }

            EntityPersister cp = persisterFactory.createEntityPersister(
                    model,
                    accessStrategy,
                    naturalIdAccessStrategy,
                    this,
                    mapping
            );
            entityPersisters.put( model.getEntityName(), cp );
            classMeta.put( model.getEntityName(), cp.getClassMetadata() );
        }
//        this.classMetadata = Collections.unmodifiableMap(classMeta);
        for (Map.Entry<String, ClassMetadata> entry : classMeta.entrySet()) {
            this.classMetadata.put(entry.getKey(), entry.getValue());
        }

        Map<String,Set<String>> tmpEntityToCollectionRoleMap = new HashMap<String,Set<String>>();
//        collectionPersisters = new HashMap<String,CollectionPersister>();
        Map<String,CollectionMetadata> tmpCollectionMetadata = new HashMap<String,CollectionMetadata>();
        Iterator collections = cfg.getCollectionMappings();
        while ( collections.hasNext() ) {
            Collection model = (Collection) collections.next();
            final String cacheRegionName = cacheRegionPrefix + model.getCacheRegionName();
            final AccessType accessType = AccessType.fromExternalName( model.getCacheConcurrencyStrategy() );
            CollectionRegionAccessStrategy accessStrategy = null;
            if ( accessType != null && settings.isSecondLevelCacheEnabled() ) {
                LOG.tracev( "Building shared cache region for collection data [{0}]", model.getRole() );
                CollectionRegion collectionRegion = regionFactory.buildCollectionRegion( cacheRegionName, properties, CacheDataDescriptionImpl
                        .decode( model ) );
                accessStrategy = collectionRegion.buildAccessStrategy( accessType );
                entityAccessStrategies.put( cacheRegionName, accessStrategy );
                cacheAccess.addCacheRegion( cacheRegionName, collectionRegion );
            }
            CollectionPersister persister = persisterFactory.createCollectionPersister(
                    cfg,
                    model,
                    accessStrategy,
                    this
            ) ;
            collectionPersisters.put( model.getRole(), persister );
            tmpCollectionMetadata.put( model.getRole(), persister.getCollectionMetadata() );
            Type indexType = persister.getIndexType();
            if ( indexType != null && indexType.isAssociationType() && !indexType.isAnyType() ) {
                String entityName = ( ( AssociationType ) indexType ).getAssociatedEntityName( this );
                Set roles = 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 = tmpEntityToCollectionRoleMap.get( entityName );
                if ( roles == null ) {
                    roles = new HashSet();
                    tmpEntityToCollectionRoleMap.put( entityName, roles );
                }
                roles.add( persister.getRole() );
            }
        }
//        collectionMetadata = Collections.unmodifiableMap( tmpCollectionMetadata );
        for (Map.Entry<String, CollectionMetadata> entry : tmpCollectionMetadata.entrySet()) {
            collectionMetadata.put(entry.getKey(), entry.getValue());
        }
        Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator();
        while ( itr.hasNext() ) {
            final Map.Entry entry = ( Map.Entry ) itr.next();
            entry.setValue( Collections.unmodifiableSet( ( Set ) entry.getValue() ) );
        }
//        collectionRolesByEntityParticipant = Collections.unmodifiableMap( tmpEntityToCollectionRoleMap );
        for (Map.Entry<String, Set<String>> entry : tmpEntityToCollectionRoleMap.entrySet()) {
            collectionRolesByEntityParticipant.put(entry.getKey(), entry.getValue());
        }

        //Named Queries:
        namedQueries.putAll(cfg.getNamedQueries());
        namedSqlQueries.putAll(cfg.getNamedSQLQueries());
        sqlResultSetMappings.putAll(cfg.getSqlResultSetMappings());
        imports.putAll(cfg.getImports());

        // after *all* persisters and named queries are registered
        Iterator iter = entityPersisters.values().iterator();
        while ( iter.hasNext() ) {
            final EntityPersister persister = ( ( EntityPersister ) iter.next() );
            persister.postInstantiate();
            registerEntityNameResolvers( persister );

        }
        iter = collectionPersisters.values().iterator();
        while ( iter.hasNext() ) {
            final CollectionPersister persister = ( ( CollectionPersister ) iter.next() );
            persister.postInstantiate();
        }

        //JNDI + Serialization:

//        name = settings.getSessionFactoryName();
//        try {
//            uuid = (String) UUID_GENERATOR.generate(null, null);
//        }
//        catch (Exception e) {
//            throw new AssertionFailure("Could not generate UUID");
//        }
//        SessionFactoryRegistry.INSTANCE.addSessionFactory(
//                uuid,
//                name,
//                settings.isSessionFactoryNameAlsoJndiName(),
//                this,
//                serviceRegistry.getService( JndiService.class )
//        );

        LOG.debug( "Instantiated session factory" );

        settings.getMultiTableBulkIdStrategy().prepare(
                jdbcServices,
                buildLocalConnectionAccess(),
                cfg.createMappings(),
                cfg.buildMapping(),
                properties
        );


        if ( settings.isAutoCreateSchema() ) {
            new SchemaExport( serviceRegistry, cfg )
                    .setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) )
                    .create( false, true );
        }
        if ( settings.isAutoUpdateSchema() ) {
            new SchemaUpdate( serviceRegistry, cfg ).execute( false, true );
        }
        if ( settings.isAutoValidateSchema() ) {
            new SchemaValidator( serviceRegistry, cfg ).validate();
        }
        if ( settings.isAutoDropSchema() ) {
            schemaExport = new SchemaExport( serviceRegistry, cfg )
                    .setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) );
        }

//        currentSessionContext = buildCurrentSessionContext();

        //checking for named queries
        if ( settings.isNamedQueryStartupCheckingEnabled() ) {
            final Map<String,HibernateException> errors = checkNamedQueries();
            if ( ! errors.isEmpty() ) {
                StringBuilder failingQueries = new StringBuilder( "Errors in named queries: " );
                String sep = "";
                for ( Map.Entry<String,HibernateException> entry : errors.entrySet() ) {
                    LOG.namedQueryError( entry.getKey(), entry.getValue() );
                    failingQueries.append( sep ).append( entry.getKey() );
                    sep = ", ";
                }
                throw new HibernateException( failingQueries.toString() );
            }
        }

        // this needs to happen after persisters are all ready to go...
//        this.fetchProfiles = new HashMap();
        itr = cfg.iterateFetchProfiles();
        while ( itr.hasNext() ) {
            final org.hibernate.mapping.FetchProfile mappingProfile =
                    ( org.hibernate.mapping.FetchProfile ) itr.next();
            final FetchProfile fetchProfile = new FetchProfile( mappingProfile.getName() );
            for ( org.hibernate.mapping.FetchProfile.Fetch mappingFetch : mappingProfile.getFetches() ) {
                // resolve the persister owning the fetch
                final String entityName = getImportedClassName( mappingFetch.getEntity() );
                final EntityPersister owner = entityName == null
                        ? null
                        : entityPersisters.get( entityName );
                if ( owner == null ) {
                    throw new HibernateException(
                            "Unable to resolve entity reference [" + mappingFetch.getEntity()
                                    + "] in fetch profile [" + fetchProfile.getName() + "]"
                    );
                }

                // validate the specified association fetch
                Type associationType = owner.getPropertyType( mappingFetch.getAssociation() );
                if ( associationType == null || !associationType.isAssociationType() ) {
                    throw new HibernateException( "Fetch profile [" + fetchProfile.getName() + "] specified an invalid association" );
                }

                // resolve the style
                final Fetch.Style fetchStyle = Fetch.Style.parse( mappingFetch.getStyle() );

                // then construct the fetch instance...
                fetchProfile.addFetch( new Association( owner, mappingFetch.getAssociation() ), fetchStyle );
                ((Loadable) owner).registerAffectingFetchProfile( fetchProfile.getName() );
            }
            fetchProfiles.put( fetchProfile.getName(), fetchProfile );
        }

//        this.customEntityDirtinessStrategy = determineCustomEntityDirtinessStrategy();
//        this.currentTenantIdentifierResolver = determineCurrentTenantIdentifierResolver( cfg.getCurrentTenantIdentifierResolver() );
//        this.transactionEnvironment = new TransactionEnvironmentImpl( this );
//        this.observer.sessionFactoryCreated( this );
    }

使用:

private SessionFactory sessionFactory;

	@Override
	protected void setUp() throws Exception {
		// A SessionFactory is set up once for an application
        sessionFactory = new Configuration()
                .configure() // configures settings from hibernate.cfg.xml
                .buildSessionFactory();

	}

	@Override
	protected void tearDown() throws Exception {
		if ( sessionFactory != null ) {
			sessionFactory.close();
		}
	}


public void testBasicCode() {
        SessionFactoryImpl sf = (SessionFactoryImpl) sessionFactory;
        Configuration cfg = new Configuration();
        cfg.addFile(new File("Code.hbm.xml"));
        cfg.buildMappings();
        sf.mergeConfiguration(cfg);
        Session session = sf.openSession();

        session.beginTransaction();
        Map code = new HashMap();
        code.put("name", "jk");
        code.put("title", "哈哈");
        session.save("Code", code);
        session.getTransaction().commit();

        session = sf.openSession();
        session.beginTransaction();
        List result = session.createQuery("from Code").list();
        for (Object obj : result) {
            System.out.println("Code ("+obj+")");
        }
        session.getTransaction().commit();

    }


© 著作权归作者所有

juqkai
粉丝 2
博文 5
码字总数 2574
作品 0
成都
高级程序员
私信 提问
【原创】基于注解运行时动态ORM映射

由于当前项目采用了分表策略,故一个实体会对应多个相同结构的表。只是映射的表名不一样而已~项目又使用憨包儿呢特(Hibernate),让我采用原生SQL总感觉不是那么爽,咋办呢?!第一念头就是...

andy_zheng
2012/11/12
1K
0
Hibernate工作原理及优势

hibernate 简介: hibernate是一个开源框架,它是对象关联关系映射的框架,它对JDBC做了轻量级的封装,而我们java程序员可以使用面向对象的思想来操纵数据库。 hibernate核心接口 session:负...

期待变强的菜鸟
2014/10/15
6.2K
0
【原创】再谈基于注解运行时动态ORM映射

上一篇贴出《基于注解运行时动态ORM映射》的方案,到底哪些地方需要用?又该怎么用呢?!我想你会有这样的疑问,其实不瞒你说,我也有!呵呵~ 再谈一把,就成为“必须的”~ 因此,本文主要介绍...

andy_zheng
2012/11/13
3.5K
8
精心整理了15道面试官喜欢问的MyBatis面试题

1、什么是 MyBatis? 答:MyBatis 是一个可以自定义 SQL、存储过程和高级映射的持久层框架。 2、讲下 MyBatis 的缓存 答:MyBatis 的缓存分为一级缓存和二级缓存,一级缓存放在 session 里面,...

Java架构师追风
07/24
0
0
Hibernate 介绍及基础应用

一、Hibernate入门基本概念 1、对象持久化: 对象是只能存在内存中,而内存不能永久的保存数据。如果要永久保存对象的状态,需要进行对象的持久化,即把对象存储到专门的数据库中。关系数据库...

Winnie007
2015/08/05
24
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
今天
1K
12
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
33
0
计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
33
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
46
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
17
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部