政务表单动态建表?运行时DDL引擎,前端拖完字段后端直接建
非科班野生程序员,深耕政务信息化20年。从VC到PB再到Java,自研框架browise也打磨了十几年。最近整理框架代码,发现不少有趣的决策,写出来和大家聊聊。最后感谢豆包、智谱、OpenCode,决策是我做的,代码是我搓的,文字是他们总结的。
场景
政务系统有个典型需求:业务人员在页面上设计表单,拖几个字段上去,点保存,后台就得在数据库里建一张物理表。不是虚拟表、不是JSON存储,是真正的物理表。因为后续要对接报表、对接工作流,没有物理表寸步难行。
而且还得支持 Oracle 和 SQL Server 两种数据库------信创改造期间两个库并行跑。
整体设计
前端拖字段 → JSON传到后端 → DDLop(入口)
↓
DDLinterface(接口)
↓ ↓
DDL_oracle_impl DDL_mssql_impl
↓ ↓
拼Oracle DDL 拼MSSQL DDL
↓
自动生成五级元数据(ea01~ea05)
第一层:DDLop --- 入口控制器
java
// DDLop.java
@aoppoint
@bean(id="ddlop")
@responseMapping(key="/ddlop")
public class DDLop {
@property(type="ref", value="ddlimpl_oracle")
DDLinterface impl ;
@Trans
@monitoring
@responseMapping(key="/createtable")
@responseType(type=String.class)
public String createtable(newDataStore<ddlDao> ddlds) throws utilException
{
impl.createtable(ddlds);
return "创建成功!";
}
@Trans
@monitoring
@responseMapping(key="/altertable")
@responseType(type=String.class)
public String altertable(newDataStore<ddlDao> ddlds) throws utilException
{
impl.alerttable(ddlds);
return "修改成功!";
}
}
注意 @property(type="ref", value="ddlimpl_oracle")------通过IoC容器注入具体实现。切数据库只改这一行配置,Oracle换成 "ddlimpl_mssql" 就行。
第二层:DDLinterface --- 统一接口
java
// DDLinterface.java
public interface DDLinterface {
public void createtable(newDataStore<ddlDao> ddlds) throws utilException;
public void alerttable(newDataStore<ddlDao> ddlds) throws utilException;
}
就两个方法:建表、改表。ddlDao 里每个字段有 columnname、datatype、datalen、iskey、des 这些属性。
第三层:DDL_oracle_impl --- Oracle建表
建表过程分四步:
第一步:拼 CREATE TABLE
java
StringBuffer buffer = new StringBuffer();
buffer.append("create table ");
String tableName = (String) ddlds.getParameter("tablename");
buffer.append(tableName);
buffer.append(" ( \r\n");
for(int i =0;i<ddlds.getRowSet().getPrimary().size();i++)
{
ddlDao dao = ddlds.getRowSet().getrow(i);
buffer.append("\t");
buffer.append(dao.getColumnname());
buffer.append(" ");
if("1".equals(dao.getDatatype()))
buffer.append("varchar2(512)");
if("2".equals(dao.getDatatype()))
buffer.append("date");
if("3".equals(dao.getDatatype()))
buffer.append("number(18,2)");
if("4".equals(dao.getDatatype()))
buffer.append("date");
if(dao.isIskey())
buffer.append(" not null");
if(i < ddlds.getRowSet().getPrimary().size() -1 )
buffer.append(",");
buffer.append(" \r\n");
}
buffer.append(" ) \r\n");
DBUtil.SaveDao(DDLMapper.class, "execsql", buffer.toString());
数据类型用编码:"1" 是字符串,"2" 是日期,"3" 是数字,"4" 是日期时间。前端传编码,后端按数据库方言翻译。
第二步:拼主键约束
java
buffer = new StringBuffer();
buffer.append("alter table ");
buffer.append(tableName);
buffer.append(" add constraint ");
buffer.append("PK_"+ tableName + " ");
buffer.append(" primary key ( ");
// ... 遍历标记了iskey的字段
buffer.append(" )");
if(count > 0)
DBUtil.SaveDao(DDLMapper.class, "execsql", buffer.toString());
第三步:加表注释和字段注释
java
String tablesql="comment on table "+tableName + " is '"+ tabledes+"'";
DBUtil.SaveDao(DDLMapper.class, "execsql", tablesql);
for(int i =0;i<ddlds.getRowSet().getPrimary().size();i++)
{
ddlDao dao = ddlds.getRowSet().getrow(i);
String sql = "comment on column "+ tableName + "."+ dao.getColumnname()
+" is '" + dao.getDes() +"'";
DBUtil.SaveDao(DDLMapper.class, "execsql", sql);
}
政务系统表注释是必须的,审计的时候说得清楚每个字段什么含义。
第四步:自动生成五级元数据
这是关键。如果表里有 proc_inst_id_ 字段(说明这张表要挂工作流),就自动生成一套元数据:
java
if(isform)
{
createsql(tableName,tabledes,ddlds);
}
createsql 方法会往 ea01~ea05 五张元数据表里写入记录:
- ea01:表级定义(表名、表描述)
- ea02:表别名映射
- ea03:字段级定义(字段名、类型、排序、注释)
- ea04:字段分组
- ea05 :默认查询条件(自动加
proc_inst_id_作为关联条件)
有了这五级元数据,前端的列表页、查询条件、表单渲染都可以自动生成,不需要手写一行SQL。
第四层:DDL_mssql_impl --- SQL Server建表
和 Oracle 实现几乎一样,区别只在数据类型映射和注释语法:
java
// Oracle: comment on table xxx is 'xxx'
// MSSQL:
String tablesql="execute sp_addextendedproperty 'MS_Description','"+tabledes+
"','user','dbo','table','"+tableName+"',null,null ";
// 字段注释
String sql = "EXECUTE sp_addextendedproperty 'MS_Description_c', '"+ dao.getDes() +
"', 'user', 'dbo', 'table', '"+tableName +
"', 'column', '"+dao.getColumnname() +"'";
SQL Server 用 sp_addextendedproperty 加注释,修改注释的时候先 add,失败了再 update:
java
try {
DBUtil.SaveDao(DDLMapper.class, "execsql", sql);
}
catch(utilException e)
{
sql = "EXECUTE sp_updateextendedproperty 'MS_Description_c', '"+ dao.getDes() +
"', 'user', 'dbo', 'table', '"+tableName +
"', 'column', '"+dao.getColumnname() +"'";
DBUtil.SaveDao(DDLMapper.class, "execsql", sql);
}
这种"先试增再改"的方式很务实,比先查后判断省一轮数据库交互。
改表支持什么
alerttable 方法支持三种操作:
- 加字段 :遍历
primary列表,isInsert()为 true 的拼ALTER TABLE ADD - 删字段 :遍历
delete列表,isModefiy()为 true 的拼ALTER TABLE DROP COLUMN - 改注释:重新写入所有字段注释
注意字段状态复用了 BaseDao 的 _t 状态机,新增=1,修改=3,删除=4。
小结
整个 DDL 引擎就三个类加一个接口,加起来不到700行代码,但解决了政务系统里"表单设计器拖字段后端自动建表"的完整链路。Oracle 和 SQL Server 切换只改一行IoC配置。五级元数据自动生成,前端列表查询表单全自动渲染。
做过动态建表的同学来聊聊,你们是怎么处理的?评论区见。
标签:#Java #DDL #动态建表 #Oracle #SQLServer #政务信息化 #自研框架