动态表单及动态建表实现原理

1 应用场景
项目中往往需要动态的创建一个表单,或者添加一个新的数据模板,这时候因为需要在运行时动态的创建表以及动态的维护表字段甚至表关系 使得普通java解决方案变得困难重重。

2 实现工具

Hibernate + Spring + Groovy +Freemarker

Hibernate 作用很简单负责创建数据库表这样可以避免我们自己去写复杂的sql和判断。

Spring 作为桥梁起到连接纽带的作用。

Groovy做为动态语言,在项目运行时根据模板创建访问数据库,或者控制层代码。

Freamker 可以根据提前定义好的模板生成 hibernate配置文件,以及Groovy代码。

3实现原理

首先创建Form 和 FromAttribute 两张表关系一对多。Form表记录表单的名称,类别,甚至是作为在动态生成表单时的css样式信息。FromAttribute记录表单字段信息,如名称,类别等。有了表单以及表单项的信息后就可以创建数据库表了。

测试代码:
public voidtestGenerator() {
Formform=formService.getAll().get(0);
List<FormAttribute>list=formAttributeService
.getAttributeListByFormId(form.getId());
form.setFormAttributeList(list);
DbGeneratordg=newDbGenerator(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 {

privateDataSourcedataSource;
protectedMaproot=newHashMap();
privatestaticLoggerlog=LoggerFactory.getLogger(FormGenerator.class);
protectedStringpath;
protectedStringpackageName;

privateFormform;

protectedConfigurationgetConfig(Stringresource){

Configurationcfg
=newConfiguration();
cfg.setDefaultEncoding(
"UTF-8");
cfg.setClassForTemplateLoading(
this.getClass(),resource);
returncfg;
}


publicDbGenerator(Formform,DataSourcedataSource){
this.form=form;
this.dataSource=dataSource;
}


publicvoidgenerator(){
if(null==form.getFormAttributeList()||form.getFormAttributeList().size()==0){
return;
}

Templatet;
try{
t
=getConfig("/template").getTemplate("hibernate.ftl");
Writerout
=newStringWriter();
t.process(getMapContext(),out);
Stringxml
=out.toString();
createTable(xml);
log.debug(xml);
}
catch(IOExceptione){
e.printStackTrace();
}
catch(TemplateExceptione){
e.printStackTrace();
}

}


@SuppressWarnings(
"unchecked")
MapgetMapContext()
{
root.put(
"entity",form);
returnroot;
}


publicvoidcreateTable(Stringxml){
org.hibernate.cfg.Configurationconf
=neworg.hibernate.cfg.Configuration();
conf.configure(
"/hibernate/hibernate.cfg.xml");
PropertiesextraProperties
=newProperties();
extraProperties.put(
"hibernate.hbm2ddl.auto","create");
conf.addProperties(extraProperties);

conf.addXML(xml);

SchemaExportdbExport;
try{
dbExport
=newSchemaExport(conf,dataSource.getConnection());
//dbExport.setOutputFile(path);
dbExport.create(false,true);
}
catch(SQLExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}

}


}


class hibernateGenerator {

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

<! DOCTYPEhibernate-mapping
PUBLIC"-//Hibernate/HibernateMappingDTD3.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" >
< generatorclass ="uuid" />
</ id >
< #ifentity.formAttributeList?exists >
< #listentity.formAttributeListasattr >
< #ifattr.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
<! DOCTYPEhibernate-configuration
PUBLIC"-//Hibernate/HibernateConfigurationDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"
>

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

< propertyname ="show_sql" > true </ property >
< propertyname ="hibernate.hbm2ddl.auto" > update </ property >

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

</ hibernate-configuration >
创建好数据库后 就要利用groovy动态创建访问代码了:先看测试代码 再看具体实现:
public void testGroovy() {
Formform
=formService.get("1");
List
<FormAttribute>list=formAttributeService
.getAttributeListByFormId(form.getId());
form.setFormAttributeList(list);
FormGeneratorfg
=newFormGenerator(form);
Stringgroovycode
=fg.generator();
ClassLoaderparent
=getClass().getClassLoader();
GroovyClassLoaderloader
=newGroovyClassLoader(parent);
ClassgroovyClass
=loader.parseClass(groovycode);
GroovyObjectgroovyObject
=null;
try{
groovyObject
=(GroovyObject)groovyClass.newInstance();
}
catch(InstantiationExceptione){
e.printStackTrace();
}
catch(IllegalAccessExceptione){
e.printStackTrace();
}

//map中key为formAttribute中描述该表单字段在数据库中的名称c_columnName
//具体情况根据formAttribute而定
Mapmap=newHashMap();
map.put(
"name","limq");
//调用insert方法插入数据
intc=(Integer)groovyObject.invokeMethod("insert",map);

//调用getAll方法获得所有动态表中的数据
Objecto=groovyObject.invokeMethod("getAll",null);
Listlist2
=(List)o;
Objectobj
=list2.get(0);
try{
Stringtname
=(String)BeanUtils.getDeclaredProperty(obj,"name");
System.out.println(tname);
}
catch(IllegalAccessExceptione){
e.printStackTrace();
}
catch(NoSuchFieldExceptione){
e.printStackTrace();
}

//调用search方法查询动态表
List<Map>returnList=(List)groovyObject.invokeMethod("search",map);
for(Mapmap2:returnList){
//同理此处根据FromAttribute而定
System.out.println(map2.get("id"));
System.out.println(map2.get(
"name"));
System.out.println(map2.get(
"type"));
}

}
FormGenerator : 创建访问数据库Groovy代码

public class FormGenerator {
protectedMaproot=newHashMap();
privatestaticLoggerlog=LoggerFactory.getLogger(FormGenerator.class);
protectedStringpath;
protectedStringpackageName;
privateFormform;
protectedConfigurationgetConfig(Stringresource){

Configurationcfg
=newConfiguration();
cfg.setDefaultEncoding(
"UTF-8");
cfg.setClassForTemplateLoading(
this.getClass(),resource);
returncfg;
}


publicFormGenerator(Formform){
this.form=form;
}


publicStringgenerator(){
Stringreturnstr
=null;
Templatet;
try{
t
=getConfig("/template").getTemplate("FormService.ftl");
//Writerout=newOutputStreamWriter(newFileOutputStream(newFile(path)),"UTF-8");
Writerout=newStringWriter();
t.process(getMapContext(),out);
returnstr
=out.toString();
log.debug(returnstr);
}
catch(IOExceptione){
e.printStackTrace();
}
catch(TemplateExceptione){
e.printStackTrace();
}

returnreturnstr;
}


@SuppressWarnings(
"unchecked")
MapgetMapContext()
{
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));
returnroot;
}

}
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 {
definsert
='${insert}'
defdelete
='${delete}'
defupdate
='${update}'
def
intinsert(entity){
defObject[]params
=[${insertParameter}]
<#assignsize=entity.formAttributeList?size/>
def
int[]types=[<#list1..size+1asp>Types.VARCHAR,<#rt/></#list>]
returnDataSourceFactory.getJdbcTemplate().update(insert,params,types)
}

def
intupdate(entity){
defObject[]params
=[${updateParameter}]
returnDataSourceFactory.getJdbcTemplate().update(update,params)
}

def
intdelete(StringentityId){
defObject[]params
=[entityId]
returnDataSourceFactory.getJdbcTemplate().update(delete,params)
}


defsearch(entity)
{
$
{query}
println(query);
returnDataSourceFactory.getJdbcTemplate().queryForList(query);

}


}


以上代码示意了如何利用 freemarker 生成 Groovy和 hibernate 相关代码,以及如何利用Groovy动态的对数据库进行创建和增删改查操作,了解以上的原理后就可以方便的在运行时利用freemarker生成表示层页面以及代码来进行展示。

你可能感兴趣的:(动态表)