之所以会做生成SQLalchemy的业务是因为自己原来的一个小程序java后台需要迁移到python,自己懒得手写每张表的映射便想用jdbc的元数据来生成。
代码量看起来比java的pojo少很多,实现的难点是在一对多的关系这里
class User(Base):
__tablename__ = 'user'
id = Column(String(20), primary_key=True)
name = Column(String(20))
# 一对多:
books = relationship('Book')
class Book(Base):
__tablename__ = 'book'
id = Column(String(20), primary_key=True)
name = Column(String(20))
# “多”的一方的book表是通过外键关联到user表的:
user_id = Column(String(20), ForeignKey('user.id'))
在pojo库模型基础上需要增加外键关系,做了些修改,一个set来存储表模型、map套list来记录外键依赖的关系
public class DataBaseModel {
private Set tableModels = new HashSet();
private Map> map = new HashMap>();
public DataBaseModel(){
Connection conn = ConnectionPool.getInstance().getConnection();
DatabaseMetaData dbmd;
try {
dbmd = conn.getMetaData();
String[] types = {"TABLE"};
//初始化map
ResultSet rs = dbmd.getTables(null, null, "%", types);
while(rs.next()){
List list = new ArrayList();
map.put(rs.getString(3), list);
}
//设置关系
rs = dbmd.getTables(null, null, "%", types);
while(rs.next()) {
TableModel tableModel = new TableModel();
String tableName = rs.getString(3);
System.out.println(tableName);
tableModel.setTableName(tableName);
//设置外键
List tableFK = new ArrayList();
ResultSet fks = dbmd.getImportedKeys(null, null, tableName);
while(fks.next()){
List temp = new ArrayList();
temp = map.get(fks.getString(3));
temp.add(fks.getString(7));
map.put(fks.getString(3), temp);
tableFK.add(new ColumnModel(fks.getString(3)+"."+fks.getString(4)));
}
tableModel.setTableFK(tableFK);
//设置主键
ColumnModel tablePK = new ColumnModel();
ResultSet pks = dbmd.getPrimaryKeys(null, null, tableName);
while(pks.next()){
System.out.println(pks.getString(4));
tablePK.setColumnName(pks.getString(4));
}
tableModel.setTablePK(tablePK);
System.out.println("-------");
tableModels.add(tableModel);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public Set getTableModels() {
return tableModels;
}
public void setTableModels(Set tableModels) {
this.tableModels = tableModels;
}
public Map> getMap() {
return map;
}
public void setMap(Map> map) {
this.map = map;
}
}
表模型也复杂了很多,需要区分主外键以及普通字段
public class TableModel {
private String tableName;
private List columns;
private ColumnModel tablePK;
private List tableFK;
private List otherColumns;
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public List getColumns() {
return columns;
}
public void setColumns(List columns) {
this.columns = columns;
}
public ColumnModel getTablePK() {
return tablePK;
}
public void setTablePK(ColumnModel tablePK) {
this.tablePK = tablePK;
}
public List getTableFK() {
return tableFK;
}
public void setTableFK(List tableFK) {
this.tableFK = tableFK;
}
public List getOtherColumns() {
return otherColumns;
}
public void setOtherColumns(List otherColumns) {
this.otherColumns = otherColumns;
}
//这个方法用于返回外键字段名组成的list以供判断
public ArrayList getTableFKStr() {
ArrayList strs = new ArrayList();
for(int i = 0;i < tableFK.size();i ++){
String columnName = tableFK.get(i).getColumnName();
String realName = columnName.substring(columnName.lastIndexOf('.')+1, columnName.length());
strs.add(realName);
}
return strs;
}
}
列模型就多了一个字段的大小
public class ColumnModel {
private String columnType;
private String columnName;
private String columnSize;
public String getColumnType() {
return columnType;
}
public void setColumnType(String columnType) {
this.columnType = columnType;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getColumnSize() {
return columnSize;
}
public void setColumnSize(String columnSize) {
this.columnSize = columnSize;
}
public ColumnModel(String columnName){
this.columnName = columnName;
}
public ColumnModel(){
}
//toString便于打印调试
@Override
public String toString() {
return "ColumnModel [columnType=" + columnType + ", columnName=" + columnName + "]";
}
}
依旧将这个过程封装到一个工具对象,分步来实现
一些相同的处理细节可以用生成pojo时用过的
private String getFirstUpperName(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
java中这几个类型名字也能用于SQLalchemy
/**
* @param type String:1-CHAR;12-VARCHAR;-1-LONGVARCHAR
* String:91-date;92-time;93-TIMESTAMP
* Integer:4-INTEGER;-5-BIGINT;5-SMALLINT;
* Double:8-DOUBLE
* FLOAT:7-REAL
* @return
*/
private String getJavaType(int type) {
if(type==1 || type==12 || type == -1
|| type==91 || type==92 || type == 93) {
return "String";
}else if(type==4 || type == -5) {
return "Integer";
}else if(type==8){
return "Double";
}else if(type==7){
return "Float";
}
System.out.println(type+"没有对应映射");
return null;
}
将代码列表书写到指定路径,也能直接拿来用
private void writeCode(String path,List codes){
path = path.replace("\\\\", "/");
File parentPathFile = new File(path.substring(0, path.lastIndexOf("/")));
if(!parentPathFile.exists()) {
parentPathFile.mkdirs();
}
BufferedWriter bw =null;
try {
bw = new BufferedWriter(new FileWriter(new File(path)));
for(String code:codes){
bw.write(code);
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(bw!=null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
基于之前的SQLalchemy分析与元数据获取,可以构造出这样的模版
public void geneSQLalchemy(String rootPath){
for(TableModel tableModel:tableModels){
List codes = new ArrayList();
//表名部分
String tableName = tableModel.getTableName();
codes.add("class "+this.getFirstUpperName(tableName)+"(Base):");
codes.add(" # 表的名字:");
codes.add(" __tablename__ = '"+tableName+"'");
//字段部分
codes.add(" # 表的结构:");
//主键
codes.add(" # 表的主键:");
if(tableModel.getTablePK().getColumnName() != null){
codes.add(" "+tableModel.getTablePK().getColumnName()+" = Column("
+tableModel.getTablePK().getColumnType()+"("
+tableModel.getTablePK().getColumnSize()+")"
+ ",primary_key=True)");
}
// 外键
codes.add(" # 表的外键:");
for(ColumnModel fk:tableModel.getTableFK()){
String localName = fk.getColumnName().substring(fk.getColumnName().lastIndexOf('.')+1, fk.getColumnName().length());
codes.add(" "+localName+" = Column("
+fk.getColumnType()+"("
+fk.getColumnSize()+")"
+ ",ForeignKey="+fk.getColumnName()+")");
}
//其他字段
codes.add(" # 其他字段:");
for(ColumnModel otherColumn:tableModel.getOtherColumns()){
codes.add(" "+otherColumn.getColumnName()+" = Column("
+otherColumn.getColumnType()+"("
+otherColumn.getColumnSize()+"))");
}
//一对多描述
codes.add(" # 一对多:");
for(String fkTableName:map.get(tableName)){
codes.add(" "+fkTableName+" = relationship('"+this.getFirstUpperName(fkTableName)+"')");
}
this.writeCode(rootPath+this.getFirstUpperName(tableName)+".py", codes);
}
}
调用看看
public static void main(String[] args) throws Exception {
GeneSQLalchemy util = new GeneSQLalchemy();
util.geneSQLalchemy("E:/testSQLalchemy/");
}