管理员可以在管理员端新建存储用户信息的数据库并建表,也可以选定已有的用户数据库来建表。之前的界面是让管理员先确定有哪些列,然后为这些列填写中文名和中文描述以及选定类型。这里的创建表不是很灵活,不能随意选择字段长度而且不能建立多个主键和外键。新迭代的功能为允许管理员使用SQL语句建表。调用SQL语句并不难,但是系统数据库需要存储新建表的字段名、类型及介绍,难点在于如何分解SQL语句得到字段名和字段类型。
SQL语句以String类型存在,建表语句中可能会有多个空格,所以用空格作为分割符不能成功拆分字段名和字段类型,而且可能还有类似“primary key”、“aoto_increment”、“not null”等补充属性扰乱第一列和第二列的分割。我想到最后还是需要工具包介入才能实现建表SQL的分解。网上几乎没有分解建表语句的教程,我根据一个分解select语句的教程编写了分解create语句的代码。项目需要引入druid包的依赖,使用这个包的类和方法分解SQL。依赖代码如下:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
项目的application.properties也需要更改:
server.port=8080
spring.datasource.name==druid
spring.datasource.type= com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.url=jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.username=root
spring.datasource.druid.password=xxxxxxxx
spring.datasource.druid.filters=stat
首先明确我想获得的是create table语句中的字段名和相应的字段类型以及字段描述。druid包中有对应不同类型SQL语句的类,比如新建数据库语句应使用SQLCreateDatabaseStatement类型,删除索引表语句应使用SQLDropIndexStatement类型,新建表应使用SQLCreateTableStatement类型,使用不对应的类型不会解析出正确的结果。我一开始就是按照解析select语句的教程使用错了对象,一直没有解析出字段名和类型。而且druid包也可以解析不同数据库的语句,这一点也要注意,不同数据库的SQL语句有些许的语法差异。解析的第一步要先声明一个SQLStatementParser类型的对象parser,我使用的是MySQL数据库,所以new出的子类型是MySqlStatementParser。第二步声明一个SQLCreateTableStatement对象sqlCreateTableStatement等于parser.parseCreateTable()。parser调用的方法也是对应create table类型的语句,其他操作要使用对应的parse方法。第三步声明一个SQLObject类型的List对象sqlObjects存储sqlCreateTableStatement的子对象,这些子对象就存储了每个字段的信息。然后使用一个for循环处理sqlObjects中每一个子对象。获取具体信息之前先判断sqlObjects实例化之后是否为SQLColumnDefinition类型,即列定义对象。如果是,则通过columnDefinition.getNameAsString()获得字段名,columnDefinition.getDataType().getName()获得字段类型,(String) ((SQLCharExpr) columnDefinition.getComment()).getValue()获得字段描述。注意这时的字段类型不包括括号内的字段长度。使用((SQLIntegerExpr) arguments.get(0)).getNumber().toString()获得字段的长度,再通过字符串拼接获得完整的字段类型。获得的信息存在一个meta对象,循环的一个meta类型的List对象。再循环执行插入DBID_CID_META表的操作。这样就完成了解析建表SQL语句的操作。具体代码如下:
public void assembleMeta(List<Meta> metaList, String sql) {
SQLStatementParser parser = new MySqlStatementParser(sql);
SQLCreateTableStatement sqlCreateTableStatement=parser.parseCreateTable();
List<SQLObject> sqlObjects = sqlCreateTableStatement.getChildren();
for (SQLObject sqlObject : sqlObjects) {
if (sqlObject instanceof SQLColumnDefinition) {
SQLColumnDefinition columnDefinition = ((SQLColumnDefinition) sqlObject);
Meta meta=new Meta();
meta.setEN(columnDefinition.getNameAsString());
System.out.println("从SQL中获得的字段名:"+meta.getEN());
String metaType=columnDefinition.getDataType().getName();
List<SQLExpr> arguments = columnDefinition.getDataType().getArguments();
if (!CollectionUtils.isEmpty(arguments)) {
metaType=metaType+"("+((SQLIntegerExpr) arguments.get(0)).getNumber().toString()+")";
}
meta.setCN(metaType);
System.out.println("从SQL中获得的字段类型:"+meta.getCN());
if (columnDefinition.getComment() != null) {
meta.setDes((String) ((SQLCharExpr) columnDefinition.getComment()).getValue());
System.out.println("从SQL中获得的字段描述:"+meta.getDes());
}else{
meta.setDes("暂无介绍!");
System.out.println("从SQL中获得的字段描述:"+meta.getDes());
}
meta.setZhengze("F");
metaList.add(meta);
}else{
System.out.println("类型还是不对!");
}
}
}
controller层的调用代码:
@ResponseBody
@RequestMapping(value = "/createSQL",method = RequestMethod.POST)
public Map<String, Object> createSQL(HttpServletRequest req) throws SQLException {
Map<String, Object> map = new HashMap<>();
int DBid =Integer.parseInt(req.getParameter("DBid").trim());
System.out.println("用户数据库编号:"+DBid);
int Cid =Integer.parseInt(req.getParameter("Cid").trim());
System.out.println("新建的表编号:"+Cid);
String sql =req.getParameter("sql").trim();
int ctsql=chartService.sqlCreateTable(DBid,sql);
System.out.println("在用户数据库建表成功!");
List<Meta> metaList=new ArrayList<>();
getFromSQL g=new getFromSQL();
g.assembleMeta(metaList,sql);
int [] um=new int[metaList.size()];
int judge=0;
for(int i=0; i<metaList.size(); i++){
um[i]=metaService.insertMeta(DBid,Cid,metaList.get(i).getEN(),metaList.get(i).getCN(),metaList.get(i).getDes());
System.out.println("um["+i+"]="+um[i]);
if(um[i]!=0)
judge=1;
}
if (ctsql==0&&judge==0) {
map.put("state", true);
map.put("msg", "使用SQL建表成功!");
} else {
map.put("state", false);
map.put("msg", "建表失败!请检查SQL语句语法问题!");
}
return map;
}