jdbc元数据自动生成SQLalchemy

文章目录

  • 前言
  • 分析SQLalchemy
  • 模型设计
    • 库模型
    • 表模型
    • 列模型
  • 工具封装
    • 细节处理
      • 处理命名
      • 处理类型
      • 书写代码
    • 构造代码
  • 结果展示

前言

之所以会做生成SQLalchemy的业务是因为自己原来的一个小程序java后台需要迁移到python,自己懒得手写每张表的映射便想用jdbc的元数据来生成。

分析SQLalchemy

代码量看起来比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/");
	}

依旧是秒出代码
jdbc元数据自动生成SQLalchemy_第1张图片
看起来也没什么问题
jdbc元数据自动生成SQLalchemy_第2张图片

你可能感兴趣的:(后端)