今天在通过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 的
感谢你的分享