【JAVA实战】手写代码生成器-根据mysql表结构生成实体类

 

项目已上传github:https://github.com/seraphapple/mysql-code-generator

这个应该属于一个入门项目吧,因为之前一直没做过,就一直想着有空了要先不上这一课,不然总觉得缺点什么。

我的实现方案其实很简单,先通过驱动和mysql数据库获取到表结构信息,再根据表名和字段名进行转换成帕斯卡命名和驼峰命名来适配类名和成员变量名,最后通过freeMarker进行组装。

具体看代码:

package com.mrh.spring.generator;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.mrh.spring.generator.model.Column;
import com.mrh.spring.generator.model.JavaDOModel;
import com.mrh.spring.generator.model.JavaDOPropModel;
import com.mrh.spring.generator.model.Table;
import com.mrh.spring.generator.util.DatabaseUtil;

import freemarker.template.Configuration;
import freemarker.template.Template;

public class JavaDOGenerator {
	
	private static final String TEMPLATE_PATH = "src/main/resource/template";
	private static final String CLASS_PATH = "src/main/java/com/mrh/spring/generator/model";
	private static final String PACKAGE = "com.mrh.spring.generator.model";

	/**
	 * 生成实体类
	 * 
	 * @param url
	 * @param username
	 * @param password
	 * @param tableName
	 */
	public static void generatorJavaDO(String url, String username, String password, String tableName) {
		Connection conn = DatabaseUtil.getConnection(url, username, password);
		Table table = DatabaseUtil.getTableColumns(conn, tableName);
		if (table == null) {
			System.out.println("table " + tableName + " has not exist");
			return;
		}
		JavaDOModel javaDOModel = convertTableToJavaDOModel(table);
		if (javaDOModel == null) {
			System.out.println("table " + tableName + " has not exist");
			return;
		}
		generator(javaDOModel, javaDOModel.getName()+".java", "javaDOTemplate.ftl");
		
		DatabaseUtil.closeConnection(conn);
	}
	
	private static void generator(Object o, String fileName, String templateName) {
		Configuration configuration = new Configuration();
        Writer out = null;
        String path = Thread.currentThread().getContextClassLoader().getResource("template").getPath();
        try {
            // step2 获取模版路径
            configuration.setDirectoryForTemplateLoading(new File(path));
            // step3 创建数据模型
            Map dataMap = new HashMap();
            dataMap.put("object", o);
            // step4 加载模版文件
            Template template = configuration.getTemplate(templateName);
            // step5 生成数据
            File docFile = new File(CLASS_PATH + "/" + fileName);
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
            // step6 输出文件
            template.process(dataMap, out);
            System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^"+fileName+" 文件创建成功 !");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != out) {
                    out.flush();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
	}

	private static JavaDOModel convertTableToJavaDOModel(Table table) {
		if (table == null) {
			return null;
		}
		JavaDOModel model = new JavaDOModel();
		model.setName(convertSnakeToPasca(table.getTableName()));
		model.setPackageStr(PACKAGE);
		List props = new ArrayList<>();
		for(Column c : table.getColumns()) {
			JavaDOPropModel prop = convertColumnToJavaDOPropModel(c);
			if(prop == null) {
				continue;
			}
			props.add(prop);
		}
		model.setProps(props);
		return model;
	}
	
	private static JavaDOPropModel convertColumnToJavaDOPropModel(Column column) {
		if(column == null) {
			return null;
		}
		JavaDOPropModel model = new JavaDOPropModel();
		model.setPropName(convertSnakeToCamel(column.getColumnName()));
		model.setPropRemark(column.getReference());
		model.setPropType(convertDatabaseTypeToJavaType(column.getColumnType()));
		model.setPascaPropName(convertCamelToPasca(model.getPropName()));
		return model;
	}
	
	private static String convertDatabaseTypeToJavaType(String dbType) {
		List integerTypes = Arrays.asList("TINYINT", "SMALLINT", "MEDIUMINT", "INT", "INTEGER");
		List longTypes = Arrays.asList("TIMESTAMP", "BIGINT");
		List floatTypes = Arrays.asList("FLOAT");
		List doubleTypes = Arrays.asList("DECIMAL");
		List stringTypes = Arrays.asList("VARCHAR", "TINYBLOB", "CHAR", "TINYTEXT", "BLOB", "TEXT", "MEDIUMBLOB", "MEDIUMTEXT", "LONGBLOB", "LONGTEXT");
		List dateTypes = Arrays.asList("DATETIME", "DATE");
		
		if(integerTypes.contains(dbType)) {
			return "Integer";
		}
		
		if(longTypes.contains(dbType)) {
			return "Long";
		}
		
		if(floatTypes.contains(dbType)) {
			return "Float";
		}
		
		if(doubleTypes.contains(dbType)) {
			return "Double";
		}
		
		if(stringTypes.contains(dbType)) {
			return "String";
		}
		
		if(dateTypes.contains(dbType)) {
			return "Date";
		}
		
		return "";
	}

	/**
	 * 由下划线(蛇型)命名法,转化成驼峰命名法
	 * 
	 * @param name
	 */
	private static String convertSnakeToCamel(String name) {
		if (name == null || name.length() == 0) {
			return name;
		}

		char[] nameChars = name.toCharArray();
		boolean previousLine = false;
		for (int i=0; i

这个就是类,因为为了图简单,所以干脆让开发人员使用main函数来调用,其实这就是为了偷懒,不要在意细节。只要功能实现了,想搞成接口什么的都很方便。

我们直接看代码吧。

public static void main(String[] args) {
		String url = "jdbc:mysql://localhost:3306/LocalTest?useUnicode=true&characterEncoding=UTF-8";
		String username = "root";
		String password = "12345678";
		
		// 初始化
		DatabaseUtil.init();
		generatorJavaDO(url, username, password, "m_user");
	}

这边调用了DatabaseUtil这个工具类,这个工具类也是我自己写的,主要就是和数据库产生连接,关闭连接和获取信息的一些方法。

DatabaseUtil.java

package com.mrh.spring.generator.util;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.mrh.spring.generator.model.Column;
import com.mrh.spring.generator.model.Table;

public class DatabaseUtil {

	/**
	 * 用main方法直接跑的话需要提前加载驱动类
	 */
	public static void init() {
		 try {
			 Class.forName("com.mysql.jdbc.Driver").newInstance();
		 } catch (InstantiationException e) {
			 e.printStackTrace();
		 } catch (IllegalAccessException e) {
			 e.printStackTrace();
		 } catch (ClassNotFoundException e) {
			 e.printStackTrace();
		 }
	}
	
	/**
	 * 获取连接
	 * @param url
	 * @param username
	 * @param password
	 * @return
	 */
	public static Connection getConnection(String url, String username, String password) {
		Connection conn = null;
		try {
			conn = DriverManager.getConnection(url, username, password);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return conn;
	}
	
	/**
	 * 操作结束后需要关闭连接
	 * @param conn
	 */
	public static void closeConnection(Connection conn) {
		try {
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 获取表结构信息并存在table里
	 * @param conn
	 * @param tableName
	 * @return
	 * @throws SQLException
	 */
	public static Table getTableColumns(Connection conn, String tableName){
		DatabaseMetaData metaData;
		Table table = null;
		try {
			metaData = conn.getMetaData();
			ResultSet rs = metaData.getColumns(null, "%", tableName, "%");
			table = new Table();
			table.setTableName(tableName);
			List columns = new ArrayList();
			while(rs.next()) {
				 Column column = new Column();
				 column.setColumnName(rs.getString("COLUMN_NAME"));
				 column.setColumnType(rs.getString("TYPE_NAME"));
				 column.setReference(rs.getString("REMARKS"));
				 columns.add(column);
			}
			table.setColumns(columns);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return table;
	}
}

其中提供了四个方法,三个我们之前都提过作用了,唯一一个没提到的是初始化,初始化是为了加载类,用main方法调用的时候需要调到。

/**
	 * 生成实体类
	 * 
	 * @param url
	 * @param username
	 * @param password
	 * @param tableName
	 */
	public static void generatorJavaDO(String url, String username, String password, String tableName) {
		Connection conn = DatabaseUtil.getConnection(url, username, password);
		Table table = DatabaseUtil.getTableColumns(conn, tableName);
		if (table == null) {
			System.out.println("table " + tableName + " has not exist");
			return;
		}
		JavaDOModel javaDOModel = convertTableToJavaDOModel(table);
		if (javaDOModel == null) {
			System.out.println("table " + tableName + " has not exist");
			return;
		}
		generator(javaDOModel, javaDOModel.getName()+".java", "javaDOTemplate.ftl");
		
		DatabaseUtil.closeConnection(conn);
	}

因为我们获取表结构信息的时候是将表结构信息存入了一个自己定义的实体类:

Table.java

package com.mrh.spring.generator.model;

import java.util.List;

public class Table {
	
	private String tableName;
	
	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;
	}

	private List columns;

}

Column也是一个实体类:

Column.java

package com.mrh.spring.generator.model;

public class Column {
	private String columnName;
	
	private String columnType;
	
	private String reference;
	
	private int nullAble;

	public String getColumnName() {
		return columnName;
	}

	public void setColumnName(String columnName) {
		this.columnName = columnName;
	}

	public String getColumnType() {
		return columnType;
	}

	public void setColumnType(String columnType) {
		this.columnType = columnType;
	}

	public String getReference() {
		return reference;
	}

	public void setReference(String reference) {
		this.reference = reference;
	}

	public int getNullAble() {
		return nullAble;
	}

	public void setNullAble(int nullAble) {
		this.nullAble = nullAble;
	}
}

这两个类里主要存放的是表的信息,而我们需要用convertTableToJavaDOModel(Table table)方法把他转换为生成实体类的JavaDOModel。

实体类属性对应的两个类为:JavaDOModel和JavaDOPropModel。

JavaDOModel.java 和 JavaDOPropModel.java

package com.mrh.spring.generator.model;

import java.util.List;

public class JavaDOModel {
	
	private String name;
	
	private List props;
	
	private String packageStr;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public List getProps() {
		return props;
	}

	public void setProps(List props) {
		this.props = props;
	}

	public String getPackageStr() {
		return packageStr;
	}

	public void setPackageStr(String packageStr) {
		this.packageStr = packageStr;
	}

}
package com.mrh.spring.generator.model;

public class JavaDOPropModel {
	private String propName;
	
	private String propType;
	
	private String propRemark;
	
	private String pascaPropName;

	public String getPropName() {
		return propName;
	}

	public void setPropName(String propName) {
		this.propName = propName;
	}

	public String getPropType() {
		return propType;
	}

	public void setPropType(String propType) {
		this.propType = propType;
	}

	public String getPropRemark() {
		return propRemark;
	}

	public void setPropRemark(String propRemark) {
		this.propRemark = propRemark;
	}

	public String getPascaPropName() {
		return pascaPropName;
	}

	public void setPascaPropName(String pascalPropName) {
		this.pascaPropName = pascalPropName;
	}
}

转换的过程也很简单:

private static JavaDOModel convertTableToJavaDOModel(Table table) {
		if (table == null) {
			return null;
		}
		JavaDOModel model = new JavaDOModel();
		model.setName(convertSnakeToPasca(table.getTableName()));
		model.setPackageStr(PACKAGE);
		List props = new ArrayList<>();
		for(Column c : table.getColumns()) {
			JavaDOPropModel prop = convertColumnToJavaDOPropModel(c);
			if(prop == null) {
				continue;
			}
			props.add(prop);
		}
		model.setProps(props);
		return model;
	}

将蛇型(也就是下划线型)命名方式的表明转换成帕斯卡命名方式的类名(也就是首字母大写的驼峰型),然后挨个对成员变量进行转换,也是将蛇型命名改成驼峰型,当然帕斯卡型也要转,用于我们生成get/set方法。对类型转换成对应的java类型,前面总的代码里都提到过了,因为数据库字段类型有限,我写的是匹配方式,比较简单暴力。

最后用freeMarker生成文件:


	private static void generator(Object o, String fileName, String templateName) {
		Configuration configuration = new Configuration();
        Writer out = null;
        String path = Thread.currentThread().getContextClassLoader().getResource("template").getPath();
        try {
            // step2 获取模版路径
            configuration.setDirectoryForTemplateLoading(new File(path));
            // step3 创建数据模型
            Map dataMap = new HashMap();
            dataMap.put("object", o);
            // step4 加载模版文件
            Template template = configuration.getTemplate(templateName);
            // step5 生成数据
            File docFile = new File(CLASS_PATH + "/" + fileName);
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
            // step6 输出文件
            template.process(dataMap, out);
            System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^"+fileName+" 文件创建成功 !");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != out) {
                    out.flush();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
	}

freeMarker生成文件是使用模板生成,模板如下:

javaDOTemplate.ftl

package ${object.packageStr};

public class ${object.name} {
	
	<#list object.props as item>
	private ${item.propType} ${item.propName};
		
	
	
	
	<#list object.props as item>
	public void set${item.pascaPropName} (${item.propType} ${item.propName}) {
		this.${item.propName} = ${item.propName};
	}
	
	
	public ${item.propType} get${item.pascaPropName} () {
		return this.${item.propName};
	}
		
	
	
}

最后我的文件就这样生成啦~

【JAVA实战】手写代码生成器-根据mysql表结构生成实体类_第1张图片

另外,我的代码里没有做如果不是基础类型的成员变量要为他们导入包这件事,但是做起来也是相当容易的,大家可以自己试试。

你可能感兴趣的:(学习笔记,实践,java,基础)