初学mybatis,然后看到他的这种运作方式,想想数据库的20几个表格,都要生成Mapper.xml、dao类、entity类、service类、web页面,20*5立马头大了,毕竟重复工作很无聊还很低效!!
于是开始百度,对于java我比较小白,但是编程的思想告诉我,重复性的劳动是给程序干的,果然找到相关的文章,这些东西是可以自动生成的,当然其中也走了弯路,最开始的想法是能连接数据库,能读数据表的结构信息(字段名、类型这些的)就自己组合下逻辑就可以自动生成实体类,毕竟这个类是最麻烦的,要把表里的字段都映射过来。
于是有了下面第一个版本,这个项目依赖很少,也不是最终我需要的,只当是练手吧,重点在后面
package com.wwt.robot;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Date;
public class GenOnlyModel {
private final String authorName = "wwt"; //作者名字
private final String packageOutPath = "com.wwt.robot.entity"; //指定实体生成所在包的路径
private final String classImplements = "implements Serializable"; //类的继承关系
private String tablename; //表名
private String[] colnames; //列名数组
private String[] colTypes; //列名类型数组
private int[] colSizes; //列名大小数组
private boolean f_util = false; //是否需要导入包java.util.*
private boolean f_sql = false; //是否需要导入包java.sql.*
private boolean f_serializable = true; //是否需要导入包java.io.Serializable
private boolean f_bigDecimal = true; //是否需要导入包java.math.BigDecimal
//数据库连接(这个是MySQL5.XXX版本的JDBC,用8.XXX版本需要修改)
private static final String URL = "jdbc:mysql://localhost:3306/wwtclrobot?useUnicode=true&characterEncoding=UTF-8";
private static final String NAME = "root";
private static final String PASS = "123456";
private static final String DRIVER = "com.mysql.jdbc.Driver";
/*
* 构造函数
*/
public GenOnlyModel(String tablename) {
//表名
this.tablename = tablename;
//创建连接
Connection con = null;
//查要生成实体类的表
String sql = "select * from " + tablename;
PreparedStatement pStemt = null;
try {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
con = DriverManager.getConnection(URL, NAME, PASS);
pStemt = con.prepareStatement(sql);
ResultSetMetaData rsmd = pStemt.getMetaData();
int size = rsmd.getColumnCount(); //统计列
colnames = new String[size];
colTypes = new String[size];
colSizes = new int[size];
for (int i = 0; i < size; i++) {
colnames[i] = rsmd.getColumnName(i + 1);
colTypes[i] = rsmd.getColumnTypeName(i + 1);
if (colTypes[i].equalsIgnoreCase("datetime") || colTypes[i].equalsIgnoreCase("date")) {
f_util = true;
}
if (colTypes[i].equalsIgnoreCase("image") || colTypes[i].equalsIgnoreCase("text")) {
f_sql = true;
}
colSizes[i] = rsmd.getColumnDisplaySize(i + 1);
}
String content = parse(colnames, colTypes, colSizes);
try {
File directory = new File("");
String outputPath = directory.getAbsolutePath() + File.separator + "entity" + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + this.packageOutPath.replace(".", File.separator) + File.separator + initcap(tablename) + ".java";
System.out.println(String.format("输出路径-%s", outputPath));
File entityFile = new File(outputPath);
if (!entityFile.exists()) {
entityFile.createNewFile();
}
FileWriter fw = new FileWriter(outputPath);
PrintWriter pw = new PrintWriter(fw);
pw.println(content);
pw.flush();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 功能:生成实体类主体代码
*
* @param colnames
* @param colTypes
* @param colSizes
* @return
*/
private String parse(String[] colnames, String[] colTypes, int[] colSizes) {
StringBuffer sb = new StringBuffer();
sb.append("package " + this.packageOutPath + ";\r\n");
sb.append("\r\n");
//判断是否导入工具包
if (f_util) {
sb.append("import java.util.Date;\r\n");
}
if (f_sql) {
sb.append("import java.sql.*;\r\n");
}
if (f_serializable) {
sb.append("import java.io.Serializable;\r\n");
}
if (f_bigDecimal) {
sb.append("import java.math.BigDecimal;;\r\n");
}
sb.append("\r\n");
//注释部分
sb.append("/**\r\n");
sb.append(" * " + tablename + " BOJO\r\n");
sb.append(" * POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。\r\n");
sb.append(" * 其中有一些属性及其getter、setter方法的类,没有业务逻辑,有时可以作为VO(value-object)或DTO(Data Transfer Object)来使用。不允许有业务方法,也不能携带connection之类的方法。\r\n");
sb.append(" * " + new Date() + " " + this.authorName + "\r\n");
sb.append("*/ \r\n");
//实体部分
sb.append("public class " + initcap(tablename) + " "+ classImplements + " {\r\n");
processAllAttrs(sb); //属性
processAllMethod(sb); //get set方法
processToString(sb); //输出打印字符串
sb.append("}\r\n");
//System.out.println(sb.toString());
return sb.toString();
}
/**
* 功能:生成所有属性
*
* @param sb
*/
private void processAllAttrs(StringBuffer sb) {
for (int i = 0; i < colnames.length; i++) {
sb.append("\tprivate " + sqlType2JavaType(colTypes[i]) + " " + colnames[i] + ";\r\n");
}
}
/**
* 功能:生成所有方法
*
* @param sb
*/
private void processAllMethod(StringBuffer sb) {
sb.append("\r\n");
for (int i = 0; i < colnames.length; i++) {
sb.append("\tpublic void set" + initcap(colnames[i]) + "(" + sqlType2JavaType(colTypes[i]) + " " +
colnames[i] + "){\r\n");
sb.append("\t\tthis." + colnames[i] + "=" + colnames[i] + ";\r\n");
sb.append("\t}\r\n");
sb.append("\tpublic " + sqlType2JavaType(colTypes[i]) + " get" + initcap(colnames[i]) + "(){\r\n");
sb.append("\t\treturn " + colnames[i] + ";\r\n");
sb.append("\t}\r\n");
}
}
/**
* 功能:生成打印输出字符串的重载函数
*/
private void processToString(StringBuffer sb) {
sb.append("\r\n");
sb.append("\t@Override\r\n");
sb.append("\tpublic String toString() {\r\n");
sb.append("\t\treturn \"" + initcap(tablename) + "{\" +\r\n");
for (int i = 0; i < colnames.length; i++) {
if(sqlType2JavaType(colTypes[i]) == "byte" || sqlType2JavaType(colTypes[i]) == "short" ||
sqlType2JavaType(colTypes[i]) == "int" || sqlType2JavaType(colTypes[i]) == "long" ||
sqlType2JavaType(colTypes[i]) == "float" || sqlType2JavaType(colTypes[i]) == "double" ||
sqlType2JavaType(colTypes[i]) == "boolean" || sqlType2JavaType(colTypes[i]) == "BigDecimal" ) {
sb.append("\t\t\t\"" + colnames[i] + "=\" + " + colnames[i] + " +\r\n");
} else if(sqlType2JavaType(colTypes[i]) == "char" || sqlType2JavaType(colTypes[i]) == "String")
{
sb.append("\t\t\t\"" + colnames[i] + "=\'\" + " + colnames[i] + " + '\\'' +\r\n");
} else{
sb.append("\t\t\t\"" + colnames[i] + "=\" + " + colnames[i] + " +\r\n");
}
}
sb.append("\t\t'}';\r\n");
sb.append("\t}\r\n");
}
/**
* 功能:将输入字符串的首字母改成大写
*
* @param str
* @return
*/
private String initcap(String str) {
char[] ch = str.toCharArray();
if (ch[0] >= 'a' && ch[0] <= 'z') {
ch[0] = (char) (ch[0] - 32);
}
return new String(ch);
}
/**
* 功能:获得列的数据类型
*
* @param sqlType
* @return
*/
private String sqlType2JavaType(String sqlType) {
if (sqlType.equalsIgnoreCase("bit")) {
return "boolean";
} else if (sqlType.equalsIgnoreCase("tinyint")) {
return "byte";
} else if (sqlType.equalsIgnoreCase("smallint")) {
return "short";
} else if (sqlType.equalsIgnoreCase("int")) {
return "int";
} else if (sqlType.equalsIgnoreCase("bigint")) {
return "long";
} else if (sqlType.equalsIgnoreCase("float")) {
return "float";
} else if (sqlType.equalsIgnoreCase("decimal")) {
return "BigDecimal";
}else if (sqlType.equalsIgnoreCase("numeric") || sqlType.equalsIgnoreCase("real")
|| sqlType.equalsIgnoreCase("money") || sqlType.equalsIgnoreCase("smallmoney")) {
return "double";
} else if (sqlType.equalsIgnoreCase("varchar") || sqlType.equalsIgnoreCase("char")
|| sqlType.equalsIgnoreCase("nvarchar") || sqlType.equalsIgnoreCase("nchar")
|| sqlType.equalsIgnoreCase("text")) {
return "String";
} else if (sqlType.equalsIgnoreCase("datetime") || sqlType.equalsIgnoreCase("date")) {
return "Date";
} else if (sqlType.equalsIgnoreCase("image")) {
return "Blod";
}
return null;
}
public static void main(String[] args) {
new GenOnlyModel("Goods");
}
}
这个方式只能生成最复杂的一项,数据表对应的实体类,其它的需要自己去写,不甘心!!!
写出来就是为了练习而已,然后开始继续找,一个工具吸引了我:
Mybatis generator自动生成dao,mapper,mapperXML
这个官网有例子,百度上面相关的教程也比较多,具体的我就不写了,有兴趣的自己去看看吧!
然后还是百度看看配置教程折腾了两天,搞定了,可以一次生成三个文件(模型+接口+映射),后来什么原因没用用这个,是因为除了这个另外还有service跟web两个文件要写,这个不能自动生成,只生成前面的东西,我20几个表,一次全做出来,不生成后面的文件,整个程序是编译不过的,没法调试,非常讨厌这种感觉,我的原则是不管对错,程序先要能编译,能跑起来,然后再来找问题,还有一个就是这个生成的文件没法自定义名称,后面一个一个自己改,还是有点小麻烦。
原本可以一个表一个表的处理,后面想想既然java是分层开发的,那还是先一层一层的处理完,再考虑上面的东西,数据库到java的映射一次搞定,于是又开始找,这次没有找到现成的东西,但是找到了一个类“tk.mybatis.mapper”借用这个类可以实现我的要求,于是又花费了三天时间,写个生成工具,现在已经能用了,我已经放到GitHub上,刚搞完,有兴趣的可以自己下载试试:
mybatis-CodeGenerator: 代码生成器,根据数据表名称生成对应的Model、Mapper、Service简化开发
期间很多坑一路踩过来,很有意思,也觉得收获挺多,有时间再写。
这里记录几个东西:
已经能实现功能之后提交了开源,然后尝试升级相关依赖
!、JDBC 5.XXX的 mysql-connector-java 升级到8.0.27版本,然后不能自动生成文件了,提示是JDBC链接方式有了改变,程序中调整了JDBC连接字符串,问题解决
2、mybatis-generator-core这个依赖升级到1.4.0之后不能用了,目前没时间解决,毕竟工具是用来方便干活的,已经放在这个上面的时间不少了,降级回1.3.5,继续升级其它的
3、hibernate-validator 尝试升级到7.0.2.Final,发现javax.validation.Valid依赖不能用了,后面降级到6.2.0.Final又可以使用,考虑到暂时没有其它地方需要使用到Hibernate-validator,这个类升级到高版本时应该已经经不再包含或者调用javax.validation-api,导致升级之后项目依赖反而不能正常使用 所以干脆去掉这个类的依赖,直接使用javax.validation-api的最高版本,测试功能正常!
这里也给我一个自己一个忠告:尽量是需要什么类用什么类,不要因为某个大类引用或者包含了你要用的几个东西,就直接拿来用,虽然这样开发会比较省事,不过将来的依赖一旦出现问题之后处理起来会比较麻烦。那天别人升级了,不包含你要引用的依赖了,你都不知道问题出在哪里~
用最小的类,能够在这个类升级的时候很容易的尝试升级他,影响的面积也小!