jdbc元数据自动生成pojo代码

前言

在阅读如下生成的代码之前,可以先看看我另一篇根据元数据获得表名、主键、外键、字段信息的博客。
https://blog.****.net/weixin_44112790/article/details/86804021

生成pojo

pojo作为数据库的映射,不难根据刚刚获得的元数据来生成pojo代码,当然这是在数据库的命名都遵循着一定规范的情况才能实现。

分析pojo

随便拿了一个pojo来看看,其中Table和Column都是我自定义的注解

@Table(value="users",pkName="userId")
public class Users {
	@Column("userId")
	private long userId;
	@Column("userName")
	private String userName;
	@Column("phoneNumber")
	private long phoneNumber;
	public Users() {
		super();
	}
	public Users(long userId, String userName, long phoneNumber) {
		super();
		this.userId = userId;
		this.userName = userName;
		this.phoneNumber = phoneNumber;
	}
	public long getUserId() {
		return userId;
	}
	public void setUserId(long userId) {
		this.userId = userId;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public long getPhoneNumber() {
		return phoneNumber;
	}
	public void setPhoneNumber(long phoneNumber) {
		this.phoneNumber = phoneNumber;
	}
	
}

可以看出代码其实就是在重复映射数据库,那么根据元数据循环起来应该很好解决;先把刚刚那个完整的代理简化一下

//表名和主键都能通过数据库的元数据得到,这里应该没什么问题
@Table(value="users",pkName="userId")
//类名只需要将表名首字母大写即可
public class Users {
	//将之前获得的字段名循环起来可以生成这个字段,值得注意的是java中的类型
	@Column("userId")
	private long userId;
	//这里只演示一个无参的构造器,有参的只需根据字段补充属性
	public Users() {
		super();
	}
	//每个属性的get、set方法,循环所得字段就能生成全部的
	public long getUserId() {
		return userId;
	}
	public void setUserId(long userId) {
		this.userId = userId;
	}
}

模型设计

根据之前的分析我们可以设计几个模型便于我们重复利用

库模型

我的想法是在创建库模型的时候,就将集合中表模型的表名和主键赋好值,在这里用一个map来存

public class DataBaseModel {
	private Map<String,String> tables = new HashMap<String,String>();
	public DataBaseModel()throws SQLException{
		Connection conn = ConnectionPool.getInstance().getConnection();
		DatabaseMetaData dbmd = conn.getMetaData();
		String[] types = {"TABLE"};
		//获得表信息
		ResultSet rs = dbmd.getTables(null, null, "%", types);
		while(rs.next()) {
			String tableName = rs.getString(3);
			ResultSet pks = dbmd.getPrimaryKeys(null, null, tableName);
			while(pks.next()){
				//主键和表名一同放入map
				tables.put(tableName, pks.getString(4));
			}
		}
	}
	public Map<String, String> getTables() {
		return tables;
	}
	public void setTables(Map<String, String> tables) {
		this.tables = tables;
	}
	
}

表模型

表模型记录表名、主键名以及字段模型

public class TableModel {
	private String tableName;
	private List<ColumnModel> columns;
	private String pkName;
	public String getTableName() {
		return tableName;
	}
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}
	public List<ColumnModel> getColumns() {
		return columns;
	}
	public void setColumns(List<ColumnModel> columns) {
		this.columns = columns;
	}
	public String getPkName() {
		return pkName;
	}
	public void setPkName(String pkName) {
		this.pkName = pkName;
	}
}

列模型

记录字段名与类型

public class ColumnModel {
	private String columnType;
	private String columnName;
	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 ColumnModel(String columnName){
		this.columnName = columnName;
	}
	public ColumnModel(){
		
	}
}

代码生成

我准备通过一个util工具对象来生成代码,其中封装一些方法,分步来实现代码的自动生成

处理类型

元数据得到的数据类型是整数代指,我这里把常用的几种考虑进去,做一个和java中类型的映射方法

/**
	 * @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.err.println(type+"没有对应映射");
		return null;
	}

处理命名

首字母大写即可

private String getFirstUpperName(String name) {
		return name.substring(0, 1).toUpperCase() + name.substring(1);
	}

初始化

我希望在初始化的时候就获得每张表的字段信息,结合库模型获得的表名主键用于生成pojo

public GenePojoUtil()throws Exception{
		//数据库模型
		DataBaseModel dbModel = new DataBaseModel();
		//获取数据库中的所有表信息
		Map<String,String> tables = dbModel.getTables();
		//获取连接
		Connection conn = ConnectionPool.getInstance().getConnection();
		for(String tableName:tables.keySet()){
			//根据sql获得表格的元数据,从而得到每个表格的字段名
			String sql =  "SELECT * FROM "+tableName+" WHERE 1 = 0";
			Statement stmt = conn.createStatement();
			ResultSet results = stmt.executeQuery(sql);
	 		ResultSetMetaData rmd = results.getMetaData();
	 		//用于存储当前表的所有字段模型
	 		List<ColumnModel> columns =  new ArrayList<ColumnModel>();
	 		for(int i = 1;i <= rmd.getColumnCount();i++){
	 			//列模型存储字段名与类型
	 			ColumnModel column = new ColumnModel();
	 			String columnName = rmd.getColumnName(i);
	 			column.setColumnName(columnName);
	 			String columnType = this.getJavaType(rmd.getColumnType(i));
	 			column.setColumnType(columnType);
	 			columns.add(column);
	 			
	 		}
	 		//将表名、主键名、字段信息存入工具类中的表模型列表
	 		TableModel tableModel = new TableModel();
	 		tableModel.setTableName(tableName);
	 		tableModel.setPkName(tables.get(tableName));
	 		tableModel.setColumns(columns);
	 		this.tableModels.add(tableModel);
		}
	}

书写代码

准备一个写代码的方法,根据路径和codes列表来生成代码

private void writeCode(String path,List<String> 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();
				}
			}
		}
	}

构造代码

将之前简化的pojo简单编辑,建议使用小红本、notepad++等高级文本编辑工具
jdbc元数据自动生成pojo代码
一部分一部分的剪切到java中并用表里替换相应的部分(注意字符串拼凑不要出问题),将构造的代码都写到一个根目录下

public void genePojo(String rootPath) throws SQLException {
		for(TableModel tableModel:tableModels){
			String tableName = tableModel.getTableName();
			List<ColumnModel> columns = tableModel.getColumns();
			List<String> codes = new ArrayList<String>();
			//包名
			codes.add("package biz.pojo;");
			//表名与主键
			codes.add("//表名和主键都能通过数据库的元数据得到,这里应该没什么问题");
			codes.add("@Table(value=\""+tableName+"\",pkName=\""+tableModel.getPkName()+"\")");
			//类名
			codes.add("//类名只需要将表名首字母大写即可");
			codes.add("public class Users {");
			//属性
			for(ColumnModel column:columns){
				String columnName = column.getColumnName();
				String columnType = column.getColumnType();
				codes.add("	@Column(+\""+columnName+"\")");
				codes.add("	private "+columnType+" "+columnName+";");
			}
			//get、set方法
			codes.add("//get、set方法");
			for(ColumnModel column:columns){
				String columnName = column.getColumnName();
				String UpName = getFirstUpperName(columnName);
				String columnType = column.getColumnType();
				codes.add("	public "+columnType+" get"+UpName+"() {");
				codes.add("		return "+columnName+";");
				codes.add("	}");
				codes.add("	public void set"+UpName+"("+columnType+" "+columnName+") {");
				codes.add("		this."+columnName+" = "+columnName+";");
				codes.add("	}");
			}
			codes.add("}");
			String path = rootPath+getFirstUpperName(tableName)+".java" ;
			this.writeCode(path, codes);
		}
		
	}

结果展示

利用刚刚的工具对象将我所连数据库的pojo全部生成到test下

public static void main(String[] args) throws Exception {
		GenePojoUtil util = new GenePojoUtil();
		util.genePojo("E:/test/");
	}

秒出代码,想想要是手写这个得花多久时间啊
jdbc元数据自动生成pojo代码
也符合之前构造的规范,和自己手写的没什么区别
jdbc元数据自动生成pojo代码