Hibernate自定义表单完全解决方案(无需重置SessionFactory)

最近开发的一个系统,需要在不更改代码和重启系统的情况下提供对用户自动建表的支持,由于系统应用了hibernate,所以在建表同时也要建立持久化对象以及对这些对象注册

数据库是不确定类型的,目前在比如数据库类型支持,还有对象关系支持上都很简单,不过在现有基础上进行扩展,都是可以实现的 
实现步骤如下 
建立class->生成hbm.xml->在Hibernate'config里面注册持久化类->通知SessionFactory持久化类的新增 
1 准备 
首先准备基础数据,我建立了几个类来对生成的表和属性做描述 这些描述都将作为传输传递给class生成方法和hbm.xml的生成方法 
RenderClass 描述要生成的实体类 属性如下

  • className 类名
  • tableName 对应表名
  • properties 属性集合


RenderProperty 就是properties集合的内容 属性如下

  • name 属性名称
  • type java类型
  • field 字段名
  • primary 是否主键
  • sequence 对应ID生成的sequence
  • length 对应长度


2 生成class 
采用ASM 生成

Java代码  复制代码
  1. /**  
  2.  * 根据传入参数创建CLASS文件  
  3.  *  
  4.  * @param 类名  
  5.  * @param 保存路径  
  6.  * @param 属性描述  
  7.  * @return Class  
  8.  */  
  9. public  Class build(String clsname,String savepath,Collection properties)   
  10. {   
  11.     Class cls = null;   
  12.     try  
  13.     {   
  14.         String classname = BuildUtil.transferClassName(clsname);   
  15.         ClassWriter cw = new ClassWriter(false);      
  16.         //建立构造函数   
  17.         cw.visit(V1_1, ACC_PUBLIC, classname, null"java/lang/Object"null);      
  18.         MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>""()V"nullnull);      
  19.         mw.visitVarInsn(ALOAD, 0);      
  20.         mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object""<init>""()V");      
  21.         mw.visitInsn(RETURN);      
  22.         mw.visitMaxs(11);      
  23.         mw.visitEnd();   
  24.            
  25.         BuildProperty property = null;   
  26.         String propertytype = null;   
  27.         String propertyname = null;;   
  28.         //建立属性   
  29.         Iterator iterator = properties.iterator();   
  30.         while (iterator.hasNext())   
  31.         {   
  32.             //建立属性对应的类变量   
  33.             property = (BuildProperty)iterator.next();   
  34.             propertytype = BuildUtil.transferClassName(property.getType());   
  35.             propertyname = WordUtils.capitalize(property.getName());   
  36.             cw.visitField(ACC_PRIVATE, property.getName(), "L"+propertytype+";"nullnull).visitEnd();   
  37.             //建立get方法   
  38.             mw = cw.visitMethod(ACC_PUBLIC, "get"+propertyname, "()L"+propertytype+";"null,    
  39.             null);    
  40.             mw.visitCode();    
  41.             mw.visitVarInsn(ALOAD, 0);    
  42.             mw    
  43.             .visitFieldInsn(GETFIELD, classname, property.getName(),    
  44.             "L"+propertytype+";");    
  45.             mw.visitInsn(ARETURN);    
  46.             mw.visitMaxs(11);    
  47.             mw.visitEnd();    
  48.             //建立set方法   
  49.             mw = cw.visitMethod(ACC_PUBLIC, "set"+propertyname, "(L"+propertytype+";)V",    
  50.                     nullnull);    
  51.             mw.visitCode();    
  52.             mw.visitVarInsn(ALOAD, 0);    
  53.             mw.visitVarInsn(ALOAD, 1);    
  54.             mw    
  55.             .visitFieldInsn(PUTFIELD, classname, property.getName(),    
  56.             "L"+propertytype+";");    
  57.             mw.visitMaxs(22);    
  58.             mw.visitInsn(RETURN);    
  59.             mw.visitEnd();    
  60.         }   
  61.            
  62.         cw.visitEnd();   
  63.   
  64.         byte[] code = cw.toByteArray();    
  65.         if (savepath!=null)   
  66.         {     
  67.             Assistant.createNewFile(savepath);   
  68.             FileOutputStream fos = new FileOutputStream(savepath);      
  69.             fos.write(code);      
  70.             fos.close();      
  71.         }   
  72.         cls = this.defineClass(clsname, code, 0, code.length);   
  73.         return cls;   
  74.     }   
  75.     catch (Throwable e)   
  76.     {   
  77.         e.printStackTrace();   
  78.     }   
  79.     return cls;   
  80. }  


3 生成hbm.xml 
生成这种事情,当然是交给模板了,我这里用的是Freemarker,在这里,我要感谢freemarker的开发组,感谢webwork 感谢XXCMS系统对freemarker以及其他模板语言的大力推广,,感谢。。。。(省略100字) 
模板嘛,肯定不仅仅是应用在web环境的,看看源代码下的freemarker.cache包,恩,不错,freemarker团队确实是有一定“野心”的 首先是渲染的实现

Java代码  复制代码
  1. package com.mit.cooperate.core.asm.render;   
  2.   
  3. import java.io.File;   
  4. import java.io.FileOutputStream;   
  5. import java.io.PrintWriter;   
  6. import java.io.StringWriter;   
  7.   
  8. import javax.servlet.ServletContext;   
  9.   
  10. import com.mit.cooperate.core.util.Assistant;   
  11.   
  12. import freemarker.cache.ClassTemplateLoader;   
  13. import freemarker.template.Configuration;   
  14. import freemarker.template.ObjectWrapper;   
  15. import freemarker.template.SimpleHash;   
  16. import freemarker.template.Template;   
  17. import freemarker.template.TemplateExceptionHandler;   
  18. /**  
  19.  * freeMarker模板渲染  
  20.  * @author courser.tijichen  
  21.  */  
  22. public class FreemarkerRender implements Render{   
  23.        
  24.     private Configuration templateconfig;   
  25.        
  26.     public  FreemarkerRender()   
  27.     {   
  28.         this.initialize();   
  29.     }   
  30.     /**  
  31.      * 初始化freemarker配置  
  32.      *  
  33.      */  
  34.     public void initialize()   
  35.     {   
  36.         try  
  37.         {   
  38.             if (templateconfig==null)   
  39.             {   
  40.                 templateconfig = new Configuration();   
  41.                 templateconfig.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);   
  42.                 templateconfig.setObjectWrapper(ObjectWrapper.DEFAULT_WRAPPER);   
  43.                 templateconfig.setTemplateLoader(new ClassTemplateLoader(this.getClass(),"/"));   
  44.                 templateconfig.setTemplateUpdateDelay(1200);   
  45.                 templateconfig.setDefaultEncoding("gb2312");   
  46.                 templateconfig.setLocale(new java.util.Locale("zh_CN"));   
  47.                 templateconfig.setNumberFormat("0.##########");   
  48.             }   
  49.         }   
  50.         catch (Exception e)   
  51.         {}   
  52.     }   
  53.     /**  
  54.      * 渲染  
  55.      *  
  56.      */  
  57.     public void render(RenderClass target,String template,String outpath)   
  58.     {   
  59.         try  
  60.         {   
  61.             StringWriter writer = new StringWriter();   
  62.             Template tl = templateconfig.getTemplate(   
  63.                     template,   
  64.                     templateconfig.getLocale());   
  65.             SimpleHash root = new SimpleHash();   
  66.             root.put("entity", target);   
  67.             tl.process(root, writer);   
  68.             Assistant.createNewFile(outpath);   
  69.             FileOutputStream fos = new FileOutputStream(outpath);   
  70.             PrintWriter pwriter = new PrintWriter(fos);   
  71.             pwriter.write(writer.toString());   
  72.             pwriter.close();   
  73.             fos.close();      
  74.         }   
  75.         catch (Exception ex)   
  76.         {   
  77.             ex.printStackTrace();   
  78.         }   
  79.     }   
  80. }  


然后就是准备模板了,首先声明,我这个模板写得简单粗陋之极,不过粗陋简单不正是人渣偶的终极处世之道么,何况只实现几个简单功能,仅仅是针对测试而已嘛,何必搞得那么正规严肃了,嘿嘿

Html代码  复制代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"   
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  4. <hibernate-mapping>  
  5.     <class name="${entity.className}" table="${entity.tableName}">  
  6.         <#if entity.properties?exists>  
  7.             <#list entity.properties as property>  
  8.                 <#if property.primary>  
  9.                     <id name="${property.name}" type="${property.type}">  
  10.                 <#else>  
  11.                     <property name="${property.name}" type="${property.type}">  
  12.                 </#if>  
  13.                 <#if property.type=="java.lang.String">  
  14.                     <column name="${property.field?upper_case}" <#if property.length?exists>length="${property.length}"</#if>></column>  
  15.                 <#elseif property.type=="java.util.Date">  
  16.                         <column name="${property.field?upper_case}" length=7></column>  
  17.                 <#elseif property.type=="java.lang.Long" || property.type=="java.lang.Integer"   
  18.                     || property.type=="java.lang.Short">  
  19.                         <column name="${property.field?upper_case}" <#if property.length?exists>precision="${property.length}"</#if> scale="0"></column>  
  20.                 </#if>  
  21.                 <#if property.primary==true>  
  22.                     <#if property.sequence?exists>  
  23.                         <generator class="sequence">  
  24.                             <param name="sequence">${property.sequence}</param>  
  25.                         </generator>  
  26.                     </#if>  
  27.                     </id>  
  28.                 <#else>  
  29.                     </property>  
  30.                 </#if>  
  31.             </#list>  
  32.         </#if>  
  33.     </class>  
  34. </hibernate-mapping>  


4 注册 
首先是对生成的hbm.xml的注册,比如,我获取倒Hibernate的一个config以后

Java代码  复制代码
  1. URL  url = this.getClass().getResource("/com/mit/test/person.hbm.xml");   
  2. config.addURL(url);  


然后就是要通知sessionFactory,新增了持久类,目前很多方法都是重启sessionfactory,就是关闭当前 sessionFactory ,然后根据config build一个新的sessionFactory出来,但是,这种情况感觉总不那么完美,虽然这个过程持续不到多长时间,但用户每增一个表就close然后build一个,单说用户体验,人家正在提交数据了,你把这个给close了.... 
但目前hibernate包的 sessionFactory 确实没提供这种对持久类的add支持,XX伟人说过:没有条件 创造条件也要上,于是乎,拿起你的键盘,启动 editplus,向hibernate3的源码砍去。。 

其实,改动地方也不大。

Java代码  复制代码
  1. 一是,改动Configuration,三句话   
  2.     public Mapping getMapping()   
  3.     {   
  4.         return this.mapping;   
  5.     }  


然后是SessionFactoryImpl 我们要让他知道,这个世界上还存在很多未知的来客,需要你去主动了解。。。。 
增加代码如下

Java代码  复制代码
  1. public void addPersistentClass(PersistentClass model,Mapping mapping)   
  2. {   
  3.     if ( !model.isInherited() ) {   
  4.         IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(   
  5.                 settings.getDialect(),   
  6.                 settings.getDefaultCatalogName(),   
  7.                 settings.getDefaultSchemaName(),   
  8.                 (RootClass) model   
  9.         );   
  10.         if (!identifierGenerators.containsKey(model.getEntityName()))   
  11.             identifierGenerators.put( model.getEntityName(), generator );   
  12.     }   
  13.        
  14.     model.prepareTemporaryTables( mapping, settings.getDialect() );   
  15.     String cacheRegion = model.getRootClass().getCacheRegionName();   
  16.   
  17.     CacheConcurrencyStrategy cache = CacheFactory.createCache(   
  18.                 model.getCacheConcurrencyStrategy(),   
  19.                 cacheRegion,   
  20.                 model.isMutable(),   
  21.                 settings,   
  22.                 properties   
  23.             );   
  24.     if (cache!=null)   
  25.         allCacheRegions.put( cache.getRegionName(), cache.getCache() );   
  26.            
  27.        
  28.     EntityPersister cp = PersisterFactory.createClassPersister(model, cache, this, mapping);   
  29.     if ( cache != null && cache.getCache() instanceof OptimisticCache ) {   
  30.         ( ( OptimisticCache ) cache.getCache() ).setSource( cp );   
  31.     }   
  32.     entityPersisters.put( model.getEntityName(), cp );   
  33. }  


最后遗留的是hbm.xml在cfg.xml的注册,以保证系统重启后可以顺利加载新增的持久化配置

Java代码  复制代码
  1. /**  
  2.  * 把hbm.xml的路径加入到cfg.xml的mapping结点  
  3.  *  
  4.  * @param cfg.xml的路径  
  5.  * @param hbm.xml的路径  
  6.  */  
  7. public static void updateHbmCfg(URL url,String hbm)   
  8. {   
  9.     try  
  10.     {   
  11.         SAXReader reader = new SAXReader();   
  12.         Document doc = reader.read(url);   
  13.         Element element = (Element)doc.getRootElement()   
  14.         .selectSingleNode("session-factory");   
  15.            
  16.         Element hbmnode = element.addElement("mapping");   
  17.         hbmnode.addAttribute("resource", hbm);   
  18.         String filepath = url.getFile();   
  19.         if (filepath.charAt(0)=='/')   
  20.             filepath = filepath.substring(1);   
  21.         FileOutputStream outputstream = new FileOutputStream(filepath);   
  22.         XMLWriter writer = new XMLWriter(outputstream);   
  23.         writer.write(doc);   
  24.         outputstream.close();   
  25.     }   
  26.     catch (Exception e)   
  27.     {   
  28.         e.printStackTrace();   
  29.     }   
  30. }  
Java代码  复制代码
  1. 大功告成 ,先运行一个小周天  


最后是总结测试,写个junit 搞定 代码如下:

Java代码  复制代码
  1. package com.mit.cooperate.core.hibernate;   
  2.   
  3. import junit.framework.TestCase;   
  4.   
  5. import java.net.URL;   
  6. import java.util.ArrayList;   
  7.   
  8. import org.apache.commons.beanutils.PropertyUtils;   
  9.   
  10. import org.hibernate.Session;   
  11. import org.hibernate.cfg.Configuration;   
  12. import org.hibernate.SessionFactory;   
  13. import org.hibernate.tool.hbm2ddl.SchemaExport;   
  14. import org.hibernate.impl.SessionFactoryImpl;   
  15. import org.hibernate.mapping.PersistentClass;   
  16. import org.hibernate.Transaction;   
  17.   
  18. import com.mit.cooperate.core.asm.*;   
  19. import com.mit.cooperate.core.asm.render.*;   
  20.   
  21. public class HibernateTest extends TestCase {   
  22.        
  23.     private Configuration config;   
  24.     private SessionFactory factory;   
  25.        
  26.     public void setUp()   
  27.     {   
  28.         URL  url = this.getClass().getResource("/com/mit/cooperate/core/hibernate/hibernate.cfg.xml");   
  29.         config = new Configuration().configure(url);   
  30.         factory = config.buildSessionFactory();   
  31.     }   
  32.        
  33.     public void testBuild() throws Exception   
  34.     {   
  35.         //持久类对象描述   
  36.         RenderClass rc = new RenderClass();   
  37.         ArrayList list = new ArrayList();   
  38.            
  39.         RenderProperty property = new RenderProperty();   
  40.         //添加主键   
  41.         property.setName("oid");   
  42.         property.setField("oid");   
  43.         property.setLength(new Integer(15));   
  44.         property.setPrimary(true);   
  45.         property.setType(Long.class.getName());   
  46.         property.setSequence("SEQ_PERSON");   
  47.            
  48.         list.add(property);   
  49.         //添加一个name字段   
  50.         property = new RenderProperty();   
  51.         property.setName("name");   
  52.         property.setType(String.class.getName());   
  53.         property.setField("name");   
  54.         property.setLength(new Integer(20));   
  55.            
  56.         list.add(property);   
  57.            
  58.         rc.setProperties(list);   
  59.         //类名   
  60.         rc.setClassName("com.mit.test.Person");   
  61.         rc.setTableName("person");   
  62.         //开始生成class   
  63.         POBuildUtil util = new POBuildUtil();   
  64.         util.build(rc.getClassName(),"E://cpc//source//cooperateCore//com//mit//test//Person.class",list);   
  65.         //实例化一个person   
  66.         Object person = Class.forName("com.mit.test.Person").newInstance();//hbmcls.newInstance();   
  67.            
  68.         //开始生成hbm.xml   
  69.         FreemarkerRender render = new FreemarkerRender();   
  70.         render.render(rc, Templates.TEMPLATE_HIBERNATE3, "E://cpc//source//cooperateCore//com//mit//test//person.hbm.xml");   
  71.         URL  url = this.getClass().getResource("/com/mit/test/person.hbm.xml");   
  72.         config.addURL(url);   
  73.         //更新hibernate.cfg.xml   
  74.         HibernateUtil.updateHbmCfg( this.getClass().getResource("/com/mit/cooperate/core/hibernate/hibernate.cfg.xml"), "com/mit/test/person.hbm.xml");   
  75.            
  76.         PersistentClass model = config.getClassMapping("com.mit.test.Person");   
  77.         //sessionFactory哪下子,快接纳person爷爷进去   
  78.         ((SessionFactoryImpl)factory).addPersistentClass(model, config.getMapping());   
  79.         //生成数据库   
  80.         SchemaExport export = new SchemaExport(config,((SessionFactoryImpl)factory).getSettings());   
  81.         export.execute(truetrue,false,true);   
  82.         //测试一下,随便给个名字什么的   
  83.         PropertyUtils.setProperty(person, "name""chenzhi");   
  84.         Session session = factory.openSession();   
  85.         Transaction tran = session.beginTransaction();   
  86.         try  
  87.         {   
  88.             //保存   
  89.             session.save(person);   
  90.             tran.commit();   
  91.         }   
  92.         catch (Exception e)   
  93.         {   
  94.             e.printStackTrace();   
  95.             tran.rollback();   
  96.         }   
  97.         finally  
  98.         {   
  99.             session.close();   
  100.         }   
  101.     }   
  102.        
  103.     public void tearDown()   
  104.     {   
  105.         factory.close();   
  106.     }   
  107.        
  108.        

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值