今天在通过sqlite数据库生成ActiveRecord model的过程中,遇到了一个问题。简单来说,就是生成的model中,所有的属性都是String类型。于是找到了这个问题:http://www.jfinal.com/feedback/261 (JFinal 与 sqlite 3 配合,生成 Model 时有误)。
波总一如既往地回复了这个问题,指出可能是sqlite-jdbc的问题。通过打断点的方式,我也跟踪了一下这个问题,发现了症结所在:

既然发现了问题,波总也给出了方案,就撸起袖子干吧!
过程中发现sqllite-jdbc对数据库元数据的支持简直是一团***。迫于无奈,只好采用了解析SQL schema的土办法。废话说,上代码:
package tech.nodex.example_springboot_restful.dao.utils;
import com.jfinal.plugin.activerecord.generator.ColumnMeta;
import com.jfinal.plugin.activerecord.generator.MetaBuilder;
import com.jfinal.plugin.activerecord.generator.TableMeta;
import javax.sql.DataSource;
import java.sql.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 通过解析SQL DML来生成model
* Created by cz on 2018-3-8.
*/
public class SqliteMetaBuilder extends MetaBuilder {
Map<String,String> sqlliteTypeMapping;
public SqliteMetaBuilder(DataSource dataSource) {
super(dataSource);
sqlliteTypeMapping = new HashMap<String,String>();
sqlliteTypeMapping.put("VARCHAR",String.class.getName());
sqlliteTypeMapping.put("NUMERIC",Integer.class.getName());
sqlliteTypeMapping.put("BIGINT",Long.class.getName());
sqlliteTypeMapping.put("INTEGER",Integer.class.getName());
sqlliteTypeMapping.put("REAL",Double.class.getName());
sqlliteTypeMapping.put("BLOB","[B");
//TODO: 补全类型列表
}
@Override
protected void buildColumnMetas(TableMeta tableMeta) throws SQLException {
PreparedStatement stm = this.conn.prepareStatement("SELECT sql FROM sqlite_master WHERE type = 'table' AND tbl_name = ?");
stm.setString(1,tableMeta.name);
ResultSet rs = stm.executeQuery();
while(rs.next()){
String dml = rs.getString(1);
int start = dml.indexOf('(')+1;
int end = dml.lastIndexOf(')');
String colsStr = dml.substring(start,end);
String[] colArray = colsStr.split(",");
for(String colMetaStr:colArray){
String[] colMetaParts = colMetaStr.trim().split("[\\s\\(\\)]");
System.out.println(Arrays.toString(colMetaParts));
ColumnMeta cm = new ColumnMeta();
cm.name = colMetaParts[0];
cm.javaType = sqlliteTypeMapping.get(colMetaParts[1]);
if(cm.javaType==null){
cm.javaType = "java.lang.String";
}
cm.attrName = this.buildAttrName(cm.name);
tableMeta.columnMetas.add(cm);
}
}
rs.close();
stm.close();
}
}在用的时候就简单了:
Generator gernerator = new Generator(dataSource, baseModelPkg, baseModelDir, modelPkg, modelDir); gernerator.setDialect(new Sqlite3Dialect()); gernerator.setMetaBuilder(new SqliteMetaBuilder(dataSource)); //关键点 gernerator.generate();
以上,问题解决。
对于希望直接拿来即用的同学,有2条善意提醒:
1) SQL schema解析没有经过大量测试,因此不敢保证完全可靠
2) 可能有些类型的映射遗漏了,或写错了,如果出现这种情况,自行修改sqliteTypeMapping中的类型映射即可。
总结
sqlite-jdbc对元数据的实现很糟糕。sqlite中的内置表sqlite_master中包含所有表的create语句。作者被逼无奈,用最笨的办法,实现了sqlite元数据的获取,给遇到同样问题的同学提供一种参考。
还可以想到一个地方,那就是 TableBuilder 会有问题,这个是用于支持 Controller 的 getBean 与 getModel 的
感谢你的分享