最近在学习一个代码生成项目,主要是针对于mybatis技术的代码生成,在此之前,我一直习惯于使用mybatis-plus以及其代码生成技术,对效率的提升很大,所以也想借此项目学习学习具体实现,由此,另开一个专题来记录一下学习历程和思考。
创建一个普通的maven后,就可以着手代码的实现,核心思路还是通过连接数据库(这里采用jdbc),执行SQL语句拿到信息,SQL语句如下图:
show table status
我用来用来测试的数据库myspace中有三张表,执行结果如下图:
我们主要关心Name字段和comment字段,至于为什么,如果你用过代码生成工具,对于生成的表所对应的po类,他需要这两个信息
此外,我们还需要定义TableInfo类,用来存放表格信息,代码详细如下:
package com.easyjava.bean;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* //表信息
* @author nangong
* @dateTime 2024/11/20 15:44
* @return
*/
public class TableInfo {
//表名
private String tableName;
//bean名称
private String beanName;
//参数名称
private String beanParamName;
//表注释
private String comment;
//字段信息
private List<FieldInfo> fieldList;
//唯一索引集合
private Map<String,List<FieldInfo>> keyIndexMap = new LinkedHashMap<>();
//是否有date类型
private Boolean haveDate;
//是否有时间类型
private Boolean haveDateTime;
//是否有bigdecimal类型
private Boolean haveBigDecimal;
public TableInfo() {
}
public TableInfo(String tableName, String beanName, String beanParamName, String comment, List<FieldInfo> fieldList, Map<String, List<FieldInfo>> keyIndexMap, Boolean haveDate, Boolean haveDateTime, Boolean haveBigDecimal) {
this.tableName = tableName;
this.beanName = beanName;
this.beanParamName = beanParamName;
this.comment = comment;
this.fieldList = fieldList;
this.keyIndexMap = keyIndexMap;
this.haveDate = haveDate;
this.haveDateTime = haveDateTime;
this.haveBigDecimal = haveBigDecimal;
}
/**
* 获取
* @return tableName
*/
public String getTableName() {
return tableName;
}
/**
* 设置
* @param tableName
*/
public void setTableName(String tableName) {
this.tableName = tableName;
}
/**
* 获取
* @return beanName
*/
public String getBeanName() {
return beanName;
}
/**
* 设置
* @param beanName
*/
public void setBeanName(String beanName) {
this.beanName = beanName;
}
/**
* 获取
* @return beanParamName
*/
public String getBeanParamName() {
return beanParamName;
}
/**
* 设置
* @param beanParamName
*/
public void setBeanParamName(String beanParamName) {
this.beanParamName = beanParamName;
}
/**
* 获取
* @return comment
*/
public String getComment() {
return comment;
}
/**
* 设置
* @param comment
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* 获取
* @return fieldList
*/
public List<FieldInfo> getFieldList() {
return fieldList;
}
/**
* 设置
* @param fieldList
*/
public void setFieldList(List<FieldInfo> fieldList) {
this.fieldList = fieldList;
}
/**
* 获取
* @return keyIndexMap
*/
public Map<String, List<FieldInfo>> getKeyIndexMap() {
return keyIndexMap;
}
/**
* 设置
* @param keyIndexMap
*/
public void setKeyIndexMap(Map<String, List<FieldInfo>> keyIndexMap) {
this.keyIndexMap = keyIndexMap;
}
/**
* 获取
* @return haveDate
*/
public Boolean getHaveDate() {
return haveDate;
}
/**
* 设置
* @param haveDate
*/
public void setHaveDate(Boolean haveDate) {
this.haveDate = haveDate;
}
/**
* 获取
* @return haveDateTime
*/
public Boolean getHaveDateTime() {
return haveDateTime;
}
/**
* 设置
* @param haveDateTime
*/
public void setHaveDateTime(Boolean haveDateTime) {
this.haveDateTime = haveDateTime;
}
/**
* 获取
* @return haveBigDecimal
*/
public Boolean getHaveBigDecimal() {
return haveBigDecimal;
}
/**
* 设置
* @param haveBigDecimal
*/
public void setHaveBigDecimal(Boolean haveBigDecimal) {
this.haveBigDecimal = haveBigDecimal;
}
public String toString() {
return "TableInfo{tableName = " + tableName + ", beanName = " + beanName + ", beanParamName = " + beanParamName + ", comment = " + comment + ", fieldList = " + fieldList + ", keyIndexMap = " + keyIndexMap + ", haveDate = " + haveDate + ", haveDateTime = " + haveDateTime + ", haveBigDecimal = " + haveBigDecimal + "}";
}
}
pom文件依赖这块,由于我们需要连接数据库,再加上一些打印日志的需求,还有一些工具类,具体依赖如下:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<!--logback(slf4j依附于其他日志框架,这里就是logback)-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.10</version>
</dependency>
<!--slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
</dependencies>
这里我们采用jdbc连接数据库,我们先在配置文件中进行基本信息的配置(这里配置文件需要用.properties,如果是yml文件,拿不到信息)
定义一个工具类,用于将配置文件中的数据以键值对的方式存入一个map中,并对外开放一个public静态方法,这样我们就可以很容易得获取到配置文件中的信息,代码如下:
package com.easyjava.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* 该工具类用于读取配置文件,并将信息存储到 PROPER_MAP
* @author nangong
* @dateTime 2024/11/20 15:43
* @param
* @return
*/
public class PropertiesUtils {
private static Properties props = new Properties();
private static Map<String,String> PROPER_MAP = new HashMap<>();
static {
InputStream is = null;
try {
is = PropertiesUtils.class.getClassLoader().getResourceAsStream("application.properties");
props.load(is);
Iterator<Object> iterator = props.keySet().iterator();
while (iterator.hasNext()){
String key = (String) iterator.next();
PROPER_MAP.put(key,props.getProperty(key));
}
}catch (Exception e){
}finally {
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static String getString(String key){
return PROPER_MAP.get(key);
}
public static void main(String[] args) {
System.out.println(getString("db.username"));
}
}
定义BuilderTable类,在此类中连接数据库并获取信息
连接数据库并初始化初始化connection对象
private static Connection conn = null;
static {
String dirverName = PropertiesUtils.getString("db.driver-class-name");
String url = PropertiesUtils.getString("db.url");
String user = PropertiesUtils.getString("db.username");
String password = PropertiesUtils.getString("db.password");
try {
Class.forName(dirverName);
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
logger.error("数据库连接失败", e);
}
}
定义好需要执行的sql语句
private static String SQL_SHOW_TABLE_STATUS = "show table status";
执行sql语句并拿到信息
PreparedStatement ps = null;
ResultSet tableResult = null;
List<TableInfo> tableInfoList = new ArrayList<>();
try {
ps = conn.prepareStatement(SQL_SHOW_TABLE_STATUS);
tableResult = ps.executeQuery();
while (tableResult.next()) {
String tableName = tableResult.getString("name");
String comment = tableResult.getString("comment");
}
信息存入tableinfo对象之前,我们需要对存入的信息细化处理:
1.针对一些数据库表命名习惯,例如:db_myspace,需要把前缀db去除吗?所以定义了以下函数:
/**
* 格式化表bean名称或者字段bean名称
*
* @param
* @return java.lang.String
* @author nangong
* @dateTime 2024/11/20 15:48
*/
private static String processFiled(String field, Boolean uperCaseFirsrLetter) {
StringBuffer sb = new StringBuffer();
String[] fields = field.split("_");
sb.append(uperCaseFirsrLetter ? StringUtils.uperCaseFirstLetter(fields[0]) : fields[0]);
for (int i = 1, len = fields.length; i < len; i++) {
sb.append(StringUtils.uperCaseFirstLetter(fields[i]));
}
return sb.toString();
}
其中StringUtils是自定义的字符串工具类,里面有两个方法,一个是把字符串第一个字符大写,另一个是小写:
package com.easyjava.utils;
import java.util.Locale;
/**
* 自定义字符串方法
* @author nangong
* @dateTime 2024/11/20 15:44
* @param
* @return
*/
public class StringUtils {
/**
* //将字符串首字符变成大写
* @author nangong
* @dateTime 2024/11/20 15:46
* @param field
* @return java.lang.String
*/
public static String uperCaseFirstLetter(String field){
if(org.apache.commons.lang3.StringUtils.isEmpty(field)){
return field;
}
return field.substring(0,1).toUpperCase()+field.substring(1);
}
/**
* //将字符串首字符变成大写
* @author nangong
* @dateTime 2024/11/20 15:46
* @param field
* @return java.lang.String
*/
public static String lowerCaseFirstLetter(String field){
if(org.apache.commons.lang3.StringUtils.isEmpty(field)){
return field;
}
return field.substring(0,1).toLowerCase()+field.substring(1);
}
}
为了让这个去除还是不去除更具有可选项,不需要通过大篇幅更改代码来改变想法,我们可以通过将其配置到配置类中
这种配置信息属于常量信息,所以我们可以定义一个常量类用来存储这类信息,用开发成本减少维护成本
/**
* 常量类
* @author nangong
* @dateTime 2024/11/20 15:45
* @return
*/
public class Constants {
public static Boolean IGNORE_TABLE_PREFIX;
public static String SUFFIX_BEAN_PARAM;
static {
IGNORE_TABLE_PREFIX = Boolean.valueOf(PropertiesUtils.getString("ignore.table.prefix"));
SUFFIX_BEAN_PARAM = PropertiesUtils.getString("suffix.bean.param");
}
}
之后,我就可以在代码中这样写:
if (Constants.IGNORE_TABLE_PREFIX) {
beanname = beanname.substring(beanname.indexOf("_") + 1);
}
此时,我们以及可以拿到TableInfo对象所需的大部分的属性了,但是没有字段信息,所以,我们还需要准备一个函数来获取字段信息
利用下面的sql语句:
show full fields from user
执行结果例子如下:
Fieldinfo类代码如下:
package com.easyjava.bean;
/**
* 字段信息
* @author nangong
* @dateTime 2024/11/20 15:44
* @return
*/
public class FieldInfo {
//字段名称
private String FieldName;
//bean属性名称
private String propertyName;
private String sqlType;
//字段类型
private String javaType;
//字段备注
private String comment;
//字段是否自增长
private Boolean isAutoIncrement;
public FieldInfo() {
}
public FieldInfo(String FieldName, String propertyName, String sqlType, String javaType, String comment, Boolean isAutoIncrement) {
this.FieldName = FieldName;
this.propertyName = propertyName;
this.sqlType = sqlType;
this.javaType = javaType;
this.comment = comment;
this.isAutoIncrement = isAutoIncrement;
}
/**
* 获取
* @return FieldName
*/
public String getFieldName() {
return FieldName;
}
/**
* 设置
* @param FieldName
*/
public void setFieldName(String FieldName) {
this.FieldName = FieldName;
}
/**
* 获取
* @return propertyName
*/
public String getPropertyName() {
return propertyName;
}
/**
* 设置
* @param propertyName
*/
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
/**
* 获取
* @return sqlType
*/
public String getSqlType() {
return sqlType;
}
/**
* 设置
* @param sqlType
*/
public void setSqlType(String sqlType) {
this.sqlType = sqlType;
}
/**
* 获取
* @return javaType
*/
public String getJavaType() {
return javaType;
}
/**
* 设置
* @param javaType
*/
public void setJavaType(String javaType) {
this.javaType = javaType;
}
/**
* 获取
* @return comment
*/
public String getComment() {
return comment;
}
/**
* 设置
* @param comment
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* 获取
* @return isAutoIncrement
*/
public Boolean getIsAutoIncrement() {
return isAutoIncrement;
}
/**
* 设置
* @param isAutoIncrement
*/
public void setIsAutoIncrement(Boolean isAutoIncrement) {
this.isAutoIncrement = isAutoIncrement;
}
public String toString() {
return "FieldInfo{FieldName = " + FieldName + ", propertyName = " + propertyName + ", sqlType = " + sqlType + ", javaType = " + javaType + ", comment = " + comment + ", isAutoIncrement = " + isAutoIncrement + "}";
}
}
思路和获取表信息一样,比较难处理就是将数据库中的数据类型转换成Java中的数据库类型,我们可以通过暴力的方法,在常量类中定义几组对应关系,不过多赘述,具体代码如下:
public final static String[] SQL_DATE_TIME_TYPES = new String[]{"datetime","timestamp"};
public final static String[] SQL_DATE_TYPES = new String[]{"date"};
public final static String[] SQL_DECIMAL_TYPES = new String[]{"decimal","double","float"};
public final static String[] SQL_STRING_TYPES = new String[]{"char","varchar","text,mediumtext","longtext"};
public final static String[] SQL_INTEGER_TYPES = new String[]{"int","tinyint"};
public final static String[] SQL_LONG_TYPES = new String[]{"bigint"};
/**
* 获取表的字段信息
*
* @param tableInfo
* @return java.util.List<com.easyjava.bean.FieldInfo>
* @author nangong
* @dateTime 2024/11/20 16:09
*/
private static List<FieldInfo> readFieldInfo(TableInfo tableInfo) {
PreparedStatement ps = null;
ResultSet fieldResult = null;
List<FieldInfo> fieldInfoList = new ArrayList<>();
try {
ps = conn.prepareStatement(String.format(SQL_SHOW_FIELDS_STATUS, tableInfo.getTableName()));
fieldResult = ps.executeQuery();
while (fieldResult.next()) {
String field = fieldResult.getString("field");
String properyName = processFiled(field, false);
String type = fieldResult.getString("type");
if (type.indexOf("(") > 0) {
type = type.substring(0, type.indexOf("("));
}
String extra = fieldResult.getString("extra");
String comment = fieldResult.getString("comment");
FieldInfo fieldInfo = new FieldInfo();
fieldInfo.setFieldName(field);
fieldInfo.setPropertyName(properyName);
fieldInfo.setComment(comment);
fieldInfo.setSqlType(type);
fieldInfo.setIsAutoIncrement("auto_increment".equalsIgnoreCase(extra)?true:false);
fieldInfo.setJavaType(processJavaType(type));
if(ArrayUtils.contains(Constants.SQL_DECIMAL_TYPES,type)){
tableInfo.setHaveBigDecimal(true);
}
if(ArrayUtils.contains(Constants.SQL_DATE_TIME_TYPES,type)){
tableInfo.setHaveDateTime(true);
}
if(ArrayUtils.contains(Constants.SQL_DATE_TYPES,type)){
tableInfo.setHaveDate(true);
}
fieldInfoList.add(fieldInfo);
logger.info("field:{},type:{},extra:{},comment:{},javatype:{}", field, type, extra, comment,fieldInfo.getJavaType());
}
} catch (Exception e) {
logger.error("读取失败", e);
} finally {
if (fieldResult != null) {
try {
fieldResult.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return fieldInfoList;
}
/**
* 将sql数据类型转化成Java数据类型
* @author nangong
* @dateTime 2024/11/20 16:33
* @param type
* @return java.lang.String
*/
private static String processJavaType(String type){
if(ArrayUtils.contains(Constants.SQL_INTEGER_TYPES,type)){
return "Integer";
}
else if(ArrayUtils.contains(Constants.SQL_LONG_TYPES,type)){
return "Long";
}
else if(ArrayUtils.contains(Constants.SQL_STRING_TYPES,type)){
return "String";
}
else if(ArrayUtils.contains(Constants.SQL_DATE_TIME_TYPES,type)||ArrayUtils.contains(Constants.SQL_DATE_TYPES,type)){
return "Date";
}
else if(ArrayUtils.contains(Constants.SQL_DECIMAL_TYPES,type)){
return "BigDecimal";
}
else{
throw new RuntimeException("无法识别类型:"+type);
}
}
package com.easyjava.builder;
import com.easyjava.bean.Constants;
import com.easyjava.bean.FieldInfo;
import com.easyjava.bean.TableInfo;
import com.easyjava.utils.PropertiesUtils;
import com.easyjava.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* jdbc建立连接,获取表信息
*
* @author nangong
* @dateTime 2024/11/20 15:44
* @return
*/
public class BuilderTable {
private static Logger logger = LoggerFactory.getLogger(BuilderTable.class);
private static Connection conn = null;
private static String SQL_SHOW_TABLE_STATUS = "show table status";
private static String SQL_SHOW_FIELDS_STATUS = "show full fields from %s";
static {
String dirverName = PropertiesUtils.getString("db.driver-class-name");
String url = PropertiesUtils.getString("db.url");
String user = PropertiesUtils.getString("db.username");
String password = PropertiesUtils.getString("db.password");
try {
Class.forName(dirverName);
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
logger.error("数据库连接失败", e);
}
}
/**
* 获取表信息
*
* @param
* @return void
* @author nangong
* @dateTime 2024/11/20 15:48
*/
public static void getTable() {
PreparedStatement ps = null;
ResultSet tableResult = null;
List<TableInfo> tableInfoList = new ArrayList<>();
try {
ps = conn.prepareStatement(SQL_SHOW_TABLE_STATUS);
tableResult = ps.executeQuery();
while (tableResult.next()) {
String tableName = tableResult.getString("name");
String comment = tableResult.getString("comment");
//logger.info("tableName:{},comment:{}", tableName, comment);
String beanname = tableName;
if (Constants.IGNORE_TABLE_PREFIX) {
beanname = beanname.substring(beanname.indexOf("_") + 1);
}
beanname = processFiled(beanname, true);
TableInfo tableInfo = new TableInfo();
tableInfo.setTableName(tableName);
tableInfo.setBeanName(beanname);
tableInfo.setComment(comment);
tableInfo.setBeanParamName(beanname + Constants.SUFFIX_BEAN_PARAM);
List<FieldInfo> fieldInfos = readFieldInfo(tableInfo);
tableInfo.setFieldList(fieldInfos);
tableInfoList.add(tableInfo);
logger.info("tablename:{},beanname:{},comment:{},beanParamName:{}", tableName, beanname, comment, beanname + Constants.SUFFIX_BEAN_PARAM);
}
} catch (Exception e) {
logger.error("读取表失败", e);
} finally {
if (tableResult != null) {
try {
tableResult.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 格式化表bean名称或者字段bean名称
*
* @param
* @return java.lang.String
* @author nangong
* @dateTime 2024/11/20 15:48
*/
private static String processFiled(String field, Boolean uperCaseFirsrLetter) {
StringBuffer sb = new StringBuffer();
String[] fields = field.split("_");
sb.append(uperCaseFirsrLetter ? StringUtils.uperCaseFirstLetter(fields[0]) : fields[0]);
for (int i = 1, len = fields.length; i < len; i++) {
sb.append(StringUtils.uperCaseFirstLetter(fields[i]));
}
return sb.toString();
}
/**
* 获取表的字段信息
*
* @param tableInfo
* @return java.util.List<com.easyjava.bean.FieldInfo>
* @author nangong
* @dateTime 2024/11/20 16:09
*/
private static List<FieldInfo> readFieldInfo(TableInfo tableInfo) {
PreparedStatement ps = null;
ResultSet fieldResult = null;
List<FieldInfo> fieldInfoList = new ArrayList<>();
try {
ps = conn.prepareStatement(String.format(SQL_SHOW_FIELDS_STATUS, tableInfo.getTableName()));
fieldResult = ps.executeQuery();
while (fieldResult.next()) {
String field = fieldResult.getString("field");
String properyName = processFiled(field, false);
String type = fieldResult.getString("type");
if (type.indexOf("(") > 0) {
type = type.substring(0, type.indexOf("("));
}
String extra = fieldResult.getString("extra");
String comment = fieldResult.getString("comment");
FieldInfo fieldInfo = new FieldInfo();
fieldInfo.setFieldName(field);
fieldInfo.setPropertyName(properyName);
fieldInfo.setComment(comment);
fieldInfo.setSqlType(type);
fieldInfo.setIsAutoIncrement("auto_increment".equalsIgnoreCase(extra)?true:false);
fieldInfo.setJavaType(processJavaType(type));
if(ArrayUtils.contains(Constants.SQL_DECIMAL_TYPES,type)){
tableInfo.setHaveBigDecimal(true);
}
if(ArrayUtils.contains(Constants.SQL_DATE_TIME_TYPES,type)){
tableInfo.setHaveDateTime(true);
}
if(ArrayUtils.contains(Constants.SQL_DATE_TYPES,type)){
tableInfo.setHaveDate(true);
}
fieldInfoList.add(fieldInfo);
logger.info("field:{},type:{},extra:{},comment:{},javatype:{}", field, type, extra, comment,fieldInfo.getJavaType());
}
} catch (Exception e) {
logger.error("读取失败", e);
} finally {
if (fieldResult != null) {
try {
fieldResult.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return fieldInfoList;
}
/**
* 将sql数据类型转化成Java数据类型
* @author nangong
* @dateTime 2024/11/20 16:33
* @param type
* @return java.lang.String
*/
private static String processJavaType(String type){
if(ArrayUtils.contains(Constants.SQL_INTEGER_TYPES,type)){
return "Integer";
}
else if(ArrayUtils.contains(Constants.SQL_LONG_TYPES,type)){
return "Long";
}
else if(ArrayUtils.contains(Constants.SQL_STRING_TYPES,type)){
return "String";
}
else if(ArrayUtils.contains(Constants.SQL_DATE_TIME_TYPES,type)||ArrayUtils.contains(Constants.SQL_DATE_TYPES,type)){
return "Date";
}
else if(ArrayUtils.contains(Constants.SQL_DECIMAL_TYPES,type)){
return "BigDecimal";
}
else{
throw new RuntimeException("无法识别类型:"+type);
}
}
}
由于内容过多,为了减少篇幅很多地方都说的很简略,如果哪里不懂,欢迎留言讨论
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuo6.cn 版权所有 赣ICP备2024042791号-9
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务