想想一个数据库这么多表自己写pojo类看看就没有写的欲望,没办法这是底层的玩意不得不写,有没有办法搞个程序让他自动生成呢?这样的话pojo类和Mybatis中的通用方法都不用自己写,一键生成想想都带劲。仔细想想其实很多的东西都是相似的,接下来教大家如何写一个代码生成器。
第一步,我们需要用一个实体类用于封装表的信息
import java.util.ArrayList;
import java.util.List;
/**
* 表信息,用于封装表的元 信息
*/
public class TableVo {
private String className; //帕斯卡风格命名
private String camelName; //骆驼风格命名(用于作为方法的参数)
private String tableName; //下划线风格的表名
private String comment; //注释
private List<ColumnVo> columns=new ArrayList<ColumnVo>(); //包含的列的对象集合
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getCamelName() {
return camelName;
}
public void setCamelName(String camelName) {
this.camelName = camelName;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public List<ColumnVo> getColumns() {
return columns;
}
public void setColumns(List<ColumnVo> columns) {
this.columns = columns;
}
}
第二步、创建一个实体类封装列的信息
/**
* 列对象,封装元信息
*/
public class ColumnVo {
private String dbName; //在数据库中的列名
private String fieldName; //java属性名
private String javaType; //java类型
private String dbType; //数据库中的类型
private String comment; //注释
private String upperCaseColumnName; //转换成为帕斯卡之后的名称,用于getter ,setteer
public String getDbName() {
return dbName;
}
public void setDbName(String dbName) {
this.dbName = dbName;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getJavaType() {
return javaType;
}
public void setJavaType(String javaType) {
this.javaType = javaType;
}
public String getDbType() {
return dbType;
}
public void setDbType(String dbType) {
this.dbType = dbType;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getUpperCaseColumnName() {
return upperCaseColumnName;
}
public void setUpperCaseColumnName(String upperCaseColumnName) {
this.upperCaseColumnName = upperCaseColumnName;
}
}
第三步、由于数据库的命名和JAVA标准命名可以不会一致,我们需要转换命名并且需要考虑到数据类型的关系转换数据的类型
比如Varchar转换为String 类型
/**
* 转换命名和数据库类型
*/
public class JavaNameUtil {
/**
* 下划线风格的命名转换为java骆驼命名法或者帕斯卡命名法
*
* @param underscoreName
* @paramisPascal是否首字母大写(帕斯卡),ture为需要转换帕斯卡,false只转换为骆驼命名法
* @return骆驼或者帕斯卡命名法的 字符串
*/
public static String translate(String underscoreName, Boolean isPascal) {
StringBuilder result = new StringBuilder();
if (underscoreName != null || underscoreName.length() > 0) {
boolean flag = false;
//首字母特殊处理,是否为帕斯卡
char fristChar = underscoreName.charAt(0);
if (isPascal) { //判断首字母是否需要处理成大写
result.append(Character.toUpperCase(fristChar));
} else {
result.append(fristChar);
}
//第二个(包含第二个)之后所有的字符
for (int i = 1; i < underscoreName.length(); i++) {
char ch = underscoreName.charAt(i);
if ('_' == ch) { //如果是下划线,不拼接
flag = true;
} else {
if (flag) { //如果遇见下划线,则转换大写追加
result.append(Character.toUpperCase(ch));
flag = false;
} else {
result.append(ch);
}
}
}
}
return result.toString();
}
//封装后的,更加容易使用,转换为帕斯卡命名法
public static String toPascal(String str) {
return translate(str, true);
}
//封装后的,更加容易使用,转换为骆驼命名法
public static String toCamel(String str) {
return translate(str, false);
}
//数据库类型名到java类型名的转换
public static String dbType2JavaType(String dbType) {
String javaType = null;
switch (dbType) {
case "VARCHAR":
javaType = "String";
break;
case "BIGINT":
javaType = "Long";
break;
case "INT":
javaType = "Integer";
break;
case "DATETIME":
javaType = "Date";
break;
default:
javaType = "String";
break;
}
return javaType;
}
}
第四步、获取我们所需要的数据的来源
就是数据的元信息,我们可以通过 DatabaseMetaData 这个sql包里面提供的类来直接获取,节省了发明这个轮子的步骤
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 负责连数据库、获取表的元信息,列的元信息
*/
public class MetadataUtil {
private static Connection conn; //连接
private static DatabaseMetaData meta;//数据库元信息
//静态块加载数据库驱动类
static{
try {
Class.forName("com.mysql.jdbc.Driver");
}catch (ClassNotFoundException e){
e.printStackTrace();
System.out.println("数据库连接失败!");
}
}
//打开连接之后,获取数据库元信息
public static void openConnection(){
try{
if(conn == null || conn.isClosed()){
conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/itripdb?useUnicode=true&characterEncoding=utf-8",
"root",
"123");
meta = conn.getMetaData();//获取数据库的元数据对象
}
}catch (SQLException e){
e.printStackTrace();
}
}
//关闭连接
public static void closeConnection(){
try {
if(conn != null && !conn.isClosed()){
conn.close();
}
}catch (SQLException e){
e.printStackTrace();
}
}
//获取所有表名称
public static String[] getTableNames(){
openConnection();
ResultSet rs = null;
List<String> nameList = new ArrayList<>();
try {
//用于信息对象,获取所有表信息
rs = meta.getTables(
null,
null,
null,
new String[] {"TABLE"});
while (rs.next()){
//只取出表信息中的每个表的名称
String tName = rs.getString("TABLE_NAME");
nameList.add(tName);
}
}catch (SQLException e){
e.printStackTrace();
}
return (String[])nameList.toArray(new String[]{});
}
public static List<String[]> getTableColumnsInfo(String tableName) throws SQLException{
openConnection();
//元信息对象,获取所有该表的列信息
ResultSet rs = meta.getColumns(null,
"%",
tableName,
"%");
List<String[]> columnInfoList = new ArrayList<>();
//从rs中获取列信息
while (rs.next()){
//针对每个列,创建一个长度为3的数组,封装3方面信息:列名、注释、数据库类型
String[] colInfo = new String[3];
colInfo[0] = rs.getString("COLUMN_NAME");
colInfo[1] = rs.getString("REMARKS");
colInfo[2] = rs.getString("TYPE_NAME");
columnInfoList.add(colInfo);
}
return columnInfoList;
}
public static Connection getConn() {
return conn;
}
public static void setConn(Connection conn) {
MetadataUtil.conn = conn;
}
public static DatabaseMetaData getMeta() {
return meta;
}
public static void setMeta(DatabaseMetaData meta) {
MetadataUtil.meta = meta;
}
}
第五步、创建一个代码生成器,提供一个公共的模板
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 代码生成器
*/
public class CodeGenerator {
protected Configuration cfg; //Freemarker配置对象
protected Map valueMap; //要填充到模板的数据,用来和ftl模板合成
protected Template template; //ftl模板对象
protected String savePath; //保存代码路径
protected List<TableVo> tableList; //元信息转换后的表信息对象集合
protected String fileNameSuffix; //生成文件名后缀
//传入模板名
public CodeGenerator(String ftl)throws Exception{
cfg=new Configuration(); //Freemarker配置对象
cfg.setClassForTemplateLoading(this.getClass(),"/templates"); //设置加载ftl模板路径
template=cfg.getTemplate(ftl); //加载ftl模板文件
valueMap=new HashMap(); //初始化
parseMetadata();
}
//获取所有表的信息,列信息,并且转换为对象
public void parseMetadata()throws Exception{
tableList=new ArrayList<>();
//获取所有表名
String[] tableNameArr=MetadataUtil.getTableNames();
for(String tName:tableNameArr){
TableVo table=new TableVo(); //表对象
//下划线转帕斯卡命名
table.setClassName(JavaNameUtil.toPascal(tName));
table.setTableName(tName);
table.setCamelName(JavaNameUtil.toCamel(tName));
//调用工具类,获取列信息
List<String[]> colInfoList=MetadataUtil.getTableColumnsInfo(tName);
for(String[] colInfo:colInfoList){ //循环把表信息转为表对象
String cName=colInfo[0]; //列名
String cComment=colInfo[1]; //列注释
String cType=colInfo[2]; //列的数据库类型
ColumnVo column=new ColumnVo();
column.setComment(cComment);
column.setDbName(cName);
//列名转java属性名
column.setFieldName(JavaNameUtil.toCamel(cName));
column.setDbType(cType);
//数据库类型转java类型
column.setJavaType(JavaNameUtil.dbType2JavaType(cType));
//列名转帕斯卡,用于getter和setter
column.setUpperCaseColumnName(JavaNameUtil.toPascal(cName));
table.getColumns().add(column); //列对象加入到表的集合属性
}
tableList.add(table); //表加入表集合
}
MetadataUtil.closeConnection();
System.out.println("构建元数据成功\n\n");
}
//生成代码的方法
public void generateCode() throws Exception{
System.out.println("---------开始生成"+template.getName()+"代码");
OutputStreamWriter writer = null;
for(TableVo table : tableList){//循环每个表对象,开始生成
//模板文件ftl中用到的${table.className}表对象就是从这里注入的
valueMap.put("table",table);
try {
//生成的每个代码文件,拼接文件名,创建一个文件写入器
writer = new FileWriter(savePath+"/"
+table.getClassName()+fileNameSuffix);
//Freemarker合成数据和模板,输出到代码文件
this.template.process(valueMap,writer);
//清空写入器缓冲
writer.flush();
}catch (TemplateException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally {
try {
writer.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
System.out.println("根据"+template.getName()+"模板生成代码成功!\n\n");
}
public void setSavePath(String savePath) {
this.savePath = savePath;
}
public void setFileNameSuffix(String fileNameSuffix) {
this.fileNameSuffix = fileNameSuffix;
}
public void setPackage(String packg){
this.valueMap.put("package",packg);
}
}
第六步、创建一个公共的ftl文件模板(以ftl为后缀的文件,里面的字段对应TableVo和ColumnVo这两个类)
pojo类的模板
package ${package}.pojo;
import java.io.Serializable;
import java.util.Date;
/**
*
*/
public class ${table.className} implements Serializable{
//生成私有属性
<#list table.columns as col>
//${col.comment}
private ${col.javaType} ${col.fieldName};
</#list>
//生成getter,setter
<#list table.columns as col>
public void set${col.upperCaseColumnName} (${col.javaType} ${col.fieldName}){
this.${col.fieldName} = ${col.fieldName};
}
public ${col.javaType} get${col.upperCaseColumnName} (){
return this.${col.fieldName};
}
</#list>
}
通用sql的模板
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${package}.dao.${table.className}Mapper">
<!-- 根据id查询单个对象 -->
<select id="get${table.className}ById" resultType="${package}.pojo.${table.className}">
select
<trim suffixOverrides=",">
<#list table.columns as col>
${col.dbName} as ${col.fieldName},
</#list>
</trim>
form ${table.tableName}
<trim prefix="where" prefixOverrides="and | or">
<if test="id != null">
and id ${r"#{"}id}
</if>
</trim>
</select>
<!-- 根据多个条件查询符合条件的数据 -->
<select id="find${table.className}ListByMap" resultType="${package}.pojo.${table.className}">
select
<trim suffixOverrides=",">
<#list table.columns as col>
${col.dbName} as ${col.fieldName},
</#list>
</trim>
from ${table.tableName}
<trim prefix="where" prefixOverrides="and | or">
<#list table.columns as col>
<if test="${col.fieldName} != null and ${col.fieldName} != ''">
and ${col.dbName} = ${r"#{"}${col.fieldName}},
</if>
</#list>
</trim>
<if test="beginPos != null and beginPos != '' and pageSize != null and pageSize != ''">
list ${r"#{"}beginPos}}, ${r"#{"}pageSize}}
</if>
</select>
<!-- 根据多个条件查询符合条件的数据总数量 -->
<select id="get${table.className}CountByMap" resultType="Integer">
select count(1)
from ${table.tableName}
<trim prefix="where" prefixOverrides="and | or">
<#list table.columns as col>
<if test="${col.fieldName} != null and ${col.fieldName} != ''">
and ${col.dbName} = ${r"#{"}${col.fieldName}},
</if>
</#list>
</trim>
</select>
<!-- 插入一个对象,返回受影响的行数 -->
<insert id="insert${table.className}" parameterType="${package}.pojo.${table.className}">
insert into ${table.tableName}(
<trim suffixOverrides=",">
<#list table.columns as col>
${r"#{"}${col.fieldName}},
</#list>
</trim>
)
values(
<trim suffixOverrides=",">
<#list table.columns as col>
${r"#{"}${col.fieldName}},
</#list>
</trim>
)
</insert>
<!-- 修改一个对象,返回受影响的行数 -->
<update id="update${table.className}" parameterType="${package}.pojo.${table.className}">
update ${table.tableName}
<trim prefix="set" suffixOverrides="," suffix="where id=${r"#{"}id}">
<#list table.columns as col>
<if test="${col.fieldName} != null and ${col.fieldName} != ''">
${col.dbName} = ${r"#{"}${col.fieldName}},
</if>
</#list>
</trim>
</update>
<!-- 删除一个对象,返回受影响的行数 -->
<delete id="delete${table.className}ById" parameterType="Integer">
delete from ${table.tableName} where id = ${r"#{"}id}
</delete>
</mapper>
mapper文件的模板(使用的Mybatis框架)
package ${package}.dao;
import ${package}.pojo.${table.className};
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.Map;
import java.util.List;
/**
* ${table.className}映射器接口
*/
public interface ${table.className}Mapper {
//根据id查询单个对象
${table.className} get${table.className}ById(@Param("id") Long id) throws RuntimeException;
//根据多个条件查询符合条件的数据
List<${table.className}> find${table.className}ListByMap(Map<String, Object> paramer) throws RuntimeException;
//根据多个条件查询符合条件的数据总数量
Integer get${table.className}CountByMap(Map<String, Object> paramer) throws RuntimeException;
//插入一个对象,返回受影响的行数
Integer insert${table.className}(${table.className} ${table.camelName}) throws RuntimeException;
//修改一个对象,返回受影响的行数
Integer update${table.className}(${table.className} ${table.camelName}) throws RuntimeException;
//删除一个对象,返回受影响的行数
Integer delete${table.className}ById(@Param("id")Long id) throws RuntimeException;
}
第七步、调用代码生成器自动生成pojo,dao,mapper文件
/**
* 代码生成器入口
*/
public class App {
public static void main(String[]args){
String myProjectPkg="cn.yunfan.itrip";
//new一个实体类生成器pojo.ftl
try {
CodeGenerator pojoGenerator=new CodeGenerator("pojo.ftl"); //设置所有模板
pojoGenerator.setSavePath("F:\\LoveToTravel\\itrip\\itrip-beans\\src\\main\\java\\cn\\yunfan\\itrip\\pojo");
pojoGenerator.setFileNameSuffix(".java"); //生成文件后缀
pojoGenerator.setPackage(myProjectPkg); //项目包名
//映射接口生成器
CodeGenerator mapperGenerator=new CodeGenerator("mapper.ftl");
mapperGenerator.setSavePath("F:\\LoveToTravel\\itrip\\itrip-dao\\src\\main\\java\\cn\\yunfan\\itrip\\dao");
mapperGenerator.setFileNameSuffix("Mapper.java");
mapperGenerator.setPackage(myProjectPkg);
//Mapper.xml生成器
CodeGenerator sqlGenerator=new CodeGenerator("sql.ftl");
sqlGenerator.setSavePath("F:\\LoveToTravel\\itrip\\itrip-dao\\src\\main\\java\\cn\\yunfan\\itrip\\dao");
sqlGenerator.setFileNameSuffix("Mapper.xml");
sqlGenerator.setPackage(myProjectPkg);
//调用三个生成器生成,分别生成1
pojoGenerator.generateCode();
mapperGenerator.generateCode();
sqlGenerator.generateCode();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注:笔者所用的是maven多模块集成开发,开发环境是IDEA,以下是CodeGenerator这个模块的manven配置文件
有需要的话可以将这个模块打压成为jar包,放在maven本地仓库,以便日后直接使用
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<dependencies>
<dependency>
<groupId>freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
</dependencies>
《这部分不是多模块开发可以不需要配置这个parent》
<parent>
<artifactId>itrip</artifactId>
<groupId>cn.yunfan</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>cn.yunfan</groupId>
<artifactId>code-generator</artifactId>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>7</source>
<target>7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
转载请注明出处,掌声送给社会人