limq

          rainman
          隨筆 - 19, 文章 - 2, 評論 - 115, 引用 - 1
          數(shù)據(jù)加載中……

          動態(tài)表單及動態(tài)建表實現(xiàn)原理

           

              1 應(yīng)用場景
            項目中往往需要動態(tài)的創(chuàng)建一個表單,或者添加一個新的數(shù)據(jù)模板,這時候因為需要在運行時動態(tài)的創(chuàng)建表以及動態(tài)的維護表字段甚至表關(guān)系 使得普通java解決方案變得困難重重。 

              2 實現(xiàn)工具

          Hibernate + Spring + Groovy +Freemarker

          Hibernate 作用很簡單負責(zé)創(chuàng)建數(shù)據(jù)庫表這樣可以避免我們自己去寫復(fù)雜的sql和判斷。

          Spring 作為橋梁起到連接紐帶的作用。

          Groovy做為動態(tài)語言,在項目運行時根據(jù)模板創(chuàng)建訪問數(shù)據(jù)庫,或者控制層代碼。

          Freamker 可以根據(jù)提前定義好的模板生成 hibernate配置文件,以及Groovy代碼。

           

              3 實現(xiàn)原理

            首先創(chuàng)建Form FromAttribute 兩張表關(guān)系一對多。Form表記錄表單的名稱,類別,甚至是作為在動態(tài)生成表單時的css樣式信息。FromAttribute記錄表單字段信息,如名稱,類別等。有了表單以及表單項的信息后就可以創(chuàng)建數(shù)據(jù)庫表了。

          測試代碼:
          public void testGenerator() {
                  Form form 
          = formService.getAll().get(0);
                  List
          <FormAttribute> list = formAttributeService
                          .getAttributeListByFormId(form.getId());
                  form.setFormAttributeList(list);
                  DbGenerator dg 
          = new DbGenerator(form, dataSource);
                  dg.generator();
              }

          DbGenerator

          import java.io.IOException;
          import java.io.StringWriter;
          import java.io.Writer;
          import java.sql.SQLException;
          import java.util.HashMap;
          import java.util.Map;
          import java.util.Properties;

          import javax.sql.DataSource;

          import org.hibernate.tool.hbm2ddl.SchemaExport;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;



          import freemarker.template.Configuration;
          import freemarker.template.Template;
          import freemarker.template.TemplateException;

          public class DbGenerator {
              
              
          private DataSource dataSource;
              
          protected Map root = new HashMap();
              
          private static Logger log = LoggerFactory.getLogger(FormGenerator.class);
              
          protected String path;
              
          protected String packageName;

              
          private Form form;

              
          protected Configuration getConfig(String resource) {

                  Configuration cfg 
          = new Configuration();
                  cfg.setDefaultEncoding(
          "UTF-8");
                  cfg.setClassForTemplateLoading(
          this.getClass(), resource);
                  
          return cfg;
              }


              
          public DbGenerator(Form form ,DataSource dataSource) {
                  
          this.form = form;
                  
          this.dataSource = dataSource;
              }


              
          public void generator() {
                  
          if(null == form.getFormAttributeList() || form.getFormAttributeList().size() == 0){
                      
          return ;
                  }

                  Template t;
                  
          try {
                      t 
          = getConfig("/template").getTemplate("hibernate.ftl");
                      Writer out 
          = new StringWriter();
                      t.process(getMapContext(), out);
                      String xml 
          = out.toString();
                      createTable(xml);
                      log.debug(xml);
                  }
           catch (IOException e) {
                      e.printStackTrace();
                  }
           catch (TemplateException e) {
                      e.printStackTrace();
                  }

              }


              @SuppressWarnings(
          "unchecked")
              Map getMapContext() 
          {
                  root.put(
          "entity", form);
                  
          return root;
              }


              
          public void createTable(String xml) {
                  org.hibernate.cfg.Configuration conf 
          = new org.hibernate.cfg.Configuration();
                  conf.configure(
          "/hibernate/hibernate.cfg.xml");
                  Properties extraProperties 
          = new Properties();
                  extraProperties.put(
          "hibernate.hbm2ddl.auto""create");
                  conf.addProperties(extraProperties);

                  conf.addXML(xml);

                  SchemaExport dbExport;
                  
          try {
                      dbExport 
          = new SchemaExport(conf, dataSource.getConnection());
                      
          // dbExport.setOutputFile(path);
                      dbExport.create(falsetrue);
                  }
           catch (SQLException e) {
                      
          // TODO Auto-generated catch block
                      e.printStackTrace();
                  }

              }


          }


          class hibernateGenerator {

          }
          hibernate.ftl
          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE hibernate-mapping 
            PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
                   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
          >

          <hibernate-mapping>
              
          <class
                  
          name="${entity.name}"
                  table
          ="`${entity.tableName}`"
                  dynamic-update
          ="false"
                  dynamic-insert
          ="false"
                  select-before-update
          ="false"
                  optimistic-lock
          ="version">
                  
          <id
                      
          name="id"
                      column
          ="id"
                      type
          ="java.lang.String"
                      unsaved-value
          ="null">
                      
          <generator class="uuid" />
                  
          </id>
                  
          <#if entity.formAttributeList?exists>
                      
          <#list entity.formAttributeList as attr>
                          
          <#if attr.name == "id">                
                          
          <#else>
                  
          <property
                      
          name="${attr.name}"
                      type
          ="java.lang.String"
                      update
          ="true"
                      insert
          ="true"
                      access
          ="property"
                      column
          ="`${attr.columnName}`"
                      length
          ="${attr.length}"
                      not-null
          ="false"
                      unique
          ="false"
                  
          />
                  
                          
          </#if>
                      
          </#list>
                  
          </#if>
                 
              
          </class>

          </hibernate-mapping>
          hibernate.cfg.xml
          <!DOCTYPE hibernate-configuration
              PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
              "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"
          >

          <hibernate-configuration>
          <session-factory>
                  
          <property name="dialect">org.hibernate.dialect.SQLServerDialect</property>
              
          <property name="connection.driver_class">net.sourceforge.jtds.jdbc.Driver</property>
              
          <property name="connection.url">jdbc:jtds:sqlserver://127.0.0.1:1433;databasename=struts;SelectMethod=cursor</property>
              
          <property name="connection.username">sa</property>
              
          <property name="connection.password">sa</property>
              
              
          <property name="show_sql">true</property>
              
          <property name="hibernate.hbm2ddl.auto">update</property>

          <!--  
              <mapping resource="hibernate/FormAttribute.hbm.xml" />
              <mapping resource="hibernate/Form.hbm.xml" />
              
          -->
          </session-factory>

          </hibernate-configuration>
           創(chuàng)建好數(shù)據(jù)庫后 就要利用groovy動態(tài)創(chuàng)建訪問代碼了:先看測試代碼 再看具體實現(xiàn):
          public void testGroovy() {
                  Form form 
          = formService.get("1");
                  List
          <FormAttribute> list = formAttributeService
                          .getAttributeListByFormId(form.getId());
                  form.setFormAttributeList(list);
                  FormGenerator fg 
          = new FormGenerator(form);
                  String groovycode 
          = fg.generator();
                  ClassLoader parent 
          = getClass().getClassLoader();
                  GroovyClassLoader loader 
          = new GroovyClassLoader(parent);
                  Class groovyClass 
          = loader.parseClass(groovycode);
                  GroovyObject groovyObject 
          = null;
                  
          try {
                      groovyObject 
          = (GroovyObject) groovyClass.newInstance();
                  }
           catch (InstantiationException e) {
                      e.printStackTrace();
                  }
           catch (IllegalAccessException e) {
                      e.printStackTrace();
                  }

                  
          // map中key為formAttribute中描述該表單字段在數(shù)據(jù)庫中的名稱c_columnName
                  
          // 具體情況根據(jù)formAttribute而定
                  Map map = new HashMap();
                  map.put(
          "name""limq");
                  
          // 調(diào)用insert方法插入數(shù)據(jù)
                  int c = (Integer) groovyObject.invokeMethod("insert", map);

                  
          // 調(diào)用getAll方法獲得所有動態(tài)表中的數(shù)據(jù)
                  Object o = groovyObject.invokeMethod("getAll"null);
                  List list2 
          = (List) o;
                  Object obj 
          = list2.get(0);
                  
          try {
                      String tname 
          = (String) BeanUtils.getDeclaredProperty(obj, "name");
                      System.out.println(tname);
                  }
           catch (IllegalAccessException e) {
                      e.printStackTrace();
                  }
           catch (NoSuchFieldException e) {
                      e.printStackTrace();
                  }

                  
          // 調(diào)用search方法查詢動態(tài)表
                  List<Map> returnList = (List) groovyObject.invokeMethod("search", map);
                  
          for (Map map2 : returnList) {
                      
          // 同理此處根據(jù)FromAttribute而定
                      System.out.println(map2.get("id"));
                      System.out.println(map2.get(
          "name"));
                      System.out.println(map2.get(
          "type"));
                  }

              }
          FormGenerator : 創(chuàng)建訪問數(shù)據(jù)庫Groovy代碼

          public class FormGenerator {
              
          protected  Map root = new HashMap();
              
          private static Logger log = LoggerFactory.getLogger(FormGenerator.class);
                  
          protected String path ;
                  
          protected String packageName ;
                  
          private Form form ; 
                  
          protected Configuration getConfig(String resource) {
                      
                       Configuration cfg 
          = new Configuration();
                      cfg.setDefaultEncoding(
          "UTF-8");
                      cfg.setClassForTemplateLoading(
          this.getClass(), resource);
                      
          return cfg;
                  }

                  
                  
          public FormGenerator(Form form){
                      
          this.form = form;
                  }

                  
                  
          public String generator(){
                      String returnstr 
          = null;
                      Template t;
                      
          try {
                          t 
          = getConfig("/template").getTemplate("FormService.ftl");
                          
          //Writer out = new OutputStreamWriter(new FileOutputStream(new File(path)),"UTF-8");
                          Writer out = new StringWriter();
                          t.process(getMapContext(), out);
                          returnstr 
          = out.toString();
                          log.debug(returnstr);
                      }
           catch (IOException e) {
                          e.printStackTrace();
                      }
           catch (TemplateException e) {
                          e.printStackTrace();
                      }

                      
          return returnstr;
                  }

                  
                  @SuppressWarnings(
          "unchecked")
                  Map getMapContext() 
          {
                      root.put(
          "entity", form);
                      root.put(
          "insert", SqlHelper.buildInsertStatement(form));
                      root.put(
          "update", SqlHelper.buildUpdateStatement(form));
                      
                      root.put(
          "insertParameter", SqlHelper.buildInsertparameter(form));
                      root.put(
          "updateParameter", SqlHelper.buildUpdateparameter(form));
                      
                      root.put(
          "delete", SqlHelper.buildDeleteStatement(form));
                      root.put(
          "query",  SqlHelper.buildQueryStatement(form));    
                      
          return root;
                  }

          }
          FormService.ftl
          import java.sql.ResultSet
          import java.sql.SQLException
          import java.sql.Types 
          import org.springframework.jdbc.core.RowMapper
          import org.springframework.jdbc.core.RowMapperResultSetExtractor
          import com.glnpu.sige.core.dao.DataSourceFactory
          import org.apache.commons.lang.builder.ToStringBuilder;
          import org.apache.commons.lang.builder.ToStringStyle;

          class ${entity.name?cap_first}Dao {
               def insert 
          = '${insert}'
               def delete 
          = '${delete}'
               def update 
          = '${update}'
               def 
          int insert( entity){
                  def Object[] params 
          = [${insertParameter}]
                  
          <#assign size = entity.formAttributeList?size/>
                  def 
          int[] types=[<#list 1..size+1 as p>Types.VARCHAR,<#rt/></#list>]
                  
          return DataSourceFactory.getJdbcTemplate().update(insert, params, types)
              }

               def 
          int update( entity){
                  def Object[] params 
          = [${updateParameter}]
                  
          return DataSourceFactory.getJdbcTemplate().update(update, params)
              }

               def 
          int delete(String entityId){
                  def Object[] params 
          =[entityId]
                  
          return DataSourceFactory.getJdbcTemplate().update(delete, params)
              }


              def search(entity)
          {
                  $
          {query}
                  println(query);
                  
          return DataSourceFactory.getJdbcTemplate().queryForList(query);
                  
              }

              
          }

           

              以上代碼示意了如何利用 freemarker 生成 Groovy 和 hibernate 相關(guān)代碼,以及如何利用Groovy動態(tài)的對數(shù)據(jù)庫進行創(chuàng)建和增刪改查操作,了解以上的原理后就可以方便的在運行時利用freemarker生成表示層頁面以及代碼來進行展示。

          posted on 2009-09-19 21:12 limq 閱讀(8538) 評論(4)  編輯  收藏 所屬分類: 編程技巧

          評論

          # re: 動態(tài)表單及動態(tài)建表實現(xiàn)原理  回復(fù)  更多評論   

          有時間試試 我們公司也有表單定義 但是確實在開發(fā)過程中使用
          正式發(fā)布后不能定義表單了
          沒有多態(tài)性可言
          2009-09-19 23:06 | wangchangbing

          # re: 動態(tài)表單及動態(tài)建表實現(xiàn)原理  回復(fù)  更多評論   

          表、代碼生成其實不難,關(guān)鍵點就是Groovy避免了熱部署問題
          2009-09-20 08:54 | 路人甲

          # re: 動態(tài)表單及動態(tài)建表實現(xiàn)原理  回復(fù)  更多評論   

          sdkf睡得很死都會發(fā)生地方
          2009-09-20 11:39 | 99讀書人

          # re: 動態(tài)表單及動態(tài)建表實現(xiàn)原理[未登錄]  回復(fù)  更多評論   

          這個解決方案非常好。
          2010-04-27 18:21 | adam
          主站蜘蛛池模板: 元氏县| 海伦市| 揭阳市| 龙岩市| 吉木萨尔县| 分宜县| 英山县| 信宜市| 合江县| 芜湖县| 科技| 文昌市| 莱阳市| 灵丘县| 辽中县| 秭归县| 界首市| 长阳| 连州市| 阿克陶县| 垦利县| 榆林市| 固安县| 福建省| 淮阳县| 平武县| 北碚区| 甘德县| 武威市| 禄劝| 湟源县| 台江县| 丘北县| 普格县| 商城县| 东丰县| 买车| 隆安县| 霍林郭勒市| 丹棱县| 南安市|