代码生成器(记录一次兴趣代码,多多指教。转载请标明作者)

在我们开始实现代码生成器之前我们先来对代码生成器有一个简单的了解。

1.什么是代码生成器?

故名思义,也就是生成代码的一个程序。那它是一个什么样的机制?怎么体现这种机制?

比如:
我们学习的 JSP 技术,浏览器并不能直接识别 JSP 文件。当用户通过浏览器访问某个 JSP 页
面的时候,其中的交互分为分为几个步骤:
 用户通过浏览器访问 JSP 页面
 Tomcat 找到对应 JSP 资源,将 JSP 资源转化(编译,翻译)成为 HTML 数据
 Tomcat 将 HTML 数据进行返回
在此,我们就可以这样理解
 我们所编写 JSP ===》 HTML 的模板。
 JSP 页面的标签 ===》模板中的占位符。
 Tomcat 根据 JSP 模板,生成 HTML 页面。  数据库的数据不同,则生成的 HTML 页面也不同。
最后我们得出一个结论:
Tomcat 可以根据 JSP 模板和数据生成不同的 HTML 页面。

所以 Tomcat 对 JSP 的处理过程我们就可以认为是一个代码生成器的机制,其实也就是代码生成器的原理。

2.我们为什么要编写代码生成器?

我们先不说它在我们软件之中处处可见,单单就作为一个程序员的辅助工具而言,也是极大的提高了开发效率。

比如我们的实体类,一个项目中表的数量不会少于二三十个吧,倘若每一个实体类,都手动编写,这种重复的代码编写,嗯,作为程序员应该都懂得。

所以,代码生成器节省人力成本,提高了开发效率。

3.开始编写代码生成器。

前面铺垫了那么多,干货终于要来了。

3.1 编写工具:idea

3.2 依赖模板引擎: FreeMarker(FreeMarker 所需要的功能简介:请点击我的另一篇博客 FreeMarker

maven依赖freemarker jar

 <!-- https://mvnrepository.com/artifact/freemarker/freemarker -->
<dependency>
<groupId>freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.8</version>
</dependency>

3.3 代码体系组成:

  • 模板:生成文件的模板文件。
  •  数据:生成文件所需要的关键数据。
  •  合成机制:使用数据置换模板中的占位符,生成新的文件的机制。

3.4 所有实现代码

---------存放数据的对象

package cn.itrip.vo;

/**
* 列对象
*/
public class ColumnVo {
private String name;//数据库中的列名
private String fieldName;//对应的Java属性名 private String dbType;//数据中的记录类型
private String javaType;//对应的Java属性类型 private String comment;//数据库中的注释
private String upperCaseColumnName;//转成帕斯卡后的属性名 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getFieldName() {
return fieldName;
} public void setFieldName(String fieldName) {
this.fieldName = fieldName;
} public String getDbType() {
return dbType;
} public void setDbType(String dbType) {
this.dbType = dbType;
} public String getJavaType() {
return javaType;
} public void setJavaType(String javaType) {
this.javaType = javaType;
} 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;
}
}
package cn.itrip.vo;

import java.util.ArrayList;
import java.util.List; /**
* 表对象
*/
public class TableVo {
private String className;//帕斯卡风格的类名
private String tableName;//表名
private String comment;//注释
private String lowerClassName;//骆驼命名法 private List<ColumnVo> columns=new ArrayList<ColumnVo>();//列对象集合 public String getLowerClassName() {
return lowerClassName;
} public void setLowerClassName(String lowerClassName) {
this.lowerClassName = lowerClassName;
} public String getClassName() {
return className;
} public void setClassName(String className) {
this.className = className;
} 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;
}
}

--------------帮助类

package cn.itrip.util;

/**
* 命名转换工具类
*/
public class JavaNameUtil {
public static String translate(String underscoreName,boolean isPascal){
StringBuilder result = new StringBuilder();//返回字符结果
if(underscoreName!=null && underscoreName.length()>0){
boolean flag=false;
char firstChar =underscoreName.charAt(0);//判断获取首字母
if(isPascal){
result.append(Character.toUpperCase(firstChar));//将首字母转换成大写
}else{
result.append(firstChar);
}
//从第二个字符开始循环
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();//返回转换字符
} /**
* 骆驼命名法
* @param str
* @return
*/
public static String toCamel(String str){
return translate(str,false);
} /**
* 帕斯卡命名法
* @param str
* @return
*/
public static String toPascal(String str){
return translate(str,true);
} /**
* 转换数据类型
* @param dbTypeName
* @return
*/
public static String dbtype2JavaType(String dbTypeName){
String javaType=null;
switch (dbTypeName){
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;
} public static void main(String[] arg){
String testName =toCamel("user_liu_jh");
System.out.println(testName);
String testName2=toPascal("user_liu_jh");
System.out.println(testName2);
}
}
package cn.itrip.util;

import java.sql.*;
import java.util.ArrayList;
import java.util.List; /**
* 负责连接数据库、获取表的元信息、列的元信息
*/
public class MetadataUtil {
private static Connection conn;//连接对象 /*2. DatabaseMetaData接口常用的方法:获取数据库元数据
(1) ResultSet getTables(String catalog,String schemaPattern,String tableNamePattern,String[] types); //获取表信息
(2) ResultSet getPrimaryKeys(String catalog,String schema,String table); //获取表主键信息
(3) ResultSet getIndexInfo(String catalog,String schema,String table,boolean unique,boolean approximate); //获取表索引信息
(4) ResultSet getColumns(String catalog,String schemaPattern,String tableNamePattern,String columnNamePattern); //获取表列信息*/
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()) {//isClosed:判断连接是否关闭
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/XXX?useUnicode=true&characterEncoding=utf-8"
, "XXX", "XXX");//连接数据库 XXX表示你自己的数据库名称 、用户名、密码
meta = conn.getMetaData();//获取元数据
}
} catch (SQLException e) {
e.printStackTrace();
}
} /**
* 获取表的注释
* @param
* @return
*/
public static String getCommentByTableName(String tableName) throws SQLException {
openConnection();//打开连接
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SHOW CREATE TABLE " + tableName);
String comment = null;
if (rs != null && rs.next()) {
comment=rs.getString(2);
//判断字符,只获取注解部分
int index = comment.lastIndexOf("=");
comment = comment.substring(index+1);
}
rs.close();
stmt.close();
conn.close();
return comment;
} /**
* 获取所有的表名
* @return
*/
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[]{});
} /**
* 获取某个表中所有的列信息
* @param tableName
* @return
* @throws Exception
*/
public static List<String[]> getTableColumnsInfo(String tableName)throws Exception{
openConnection();
ResultSet rs= meta.getColumns(null,
"%",tableName,"%");
List<String[]> columnInfoList =new ArrayList<>();
while (rs.next()){
String[] colInfo = new String[3];
colInfo[0]=rs.getString("COLUMN_NAME");//COLUMN_NAME 列名
colInfo[1]=rs.getString("REMARKS");//REMARKS 获取列注释
colInfo[2]=rs.getString("TYPE_NAME");//TYPE_NAME 列类型
columnInfoList.add(colInfo);
}
return columnInfoList;
}
}

---------合成机制

package cn.itrip.generator;

import cn.itrip.util.JavaNameUtil;
import cn.itrip.util.MetadataUtil;
import cn.itrip.vo.ColumnVo;
import cn.itrip.vo.TableVo;
import freemarker.template.Configuration;
import freemarker.template.Template; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* 抽象生成器
*/
public abstract class AbstractGenerator {
protected Configuration cfg;//Freemarker配置对象
protected Map valueMap;//填充到模板的数据
protected Template template;//模板对象
protected String savePath;//保存生成文件的路径
protected List<TableVo> tableList;//表信息 public AbstractGenerator() throws Exception {
cfg = new Configuration();
cfg.setClassForTemplateLoading(this.getClass(), "/templates/"); //获取模板文件位置
valueMap = new HashMap();
init();//初始化数据
} /**
* 获取所有的表信息列信息
* @throws Exception
*/
public void init()throws Exception{
tableList=new ArrayList<>();
String[] tableNameArr =MetadataUtil.getTableNames();//获取表名称
for(String tName:tableNameArr){
String tComment = MetadataUtil.getCommentByTableName(tName);//获取注释
TableVo table = new TableVo();//创建表信息对象
table.setClassName(JavaNameUtil.toPascal(tName));
table.setComment(tComment);
table.setTableName(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.setName(cName);
column.setFieldName(JavaNameUtil.toCamel(cName));
column.setDbType(cType);
column.setJavaType(JavaNameUtil.dbtype2JavaType(cType));
column.setUpperCaseColumnName(JavaNameUtil.toPascal(cName));
table.getColumns().add(column);
}
tableList.add(table);
System.out.println(table);
}
System.out.println("构建元数据成功!");
} //抽象生成代码文件,各个子类实现
public abstract void generateCode() throws Exception; public void setSavePath(String savePath) {
this.savePath = savePath;
}
}
package cn.itrip.generator;

import cn.itrip.vo.TableVo;
import freemarker.template.TemplateException; import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter; /**
* 实体类生成器
*/
public class EntityGenerator extends AbstractGenerator{ public EntityGenerator(String ftl) throws Exception {
try{
this.savePath="D:\\object\\itripljh\\itripbeans\\src\\main\\java\\cn\\itrip\\pojo";
//加载freemarker模板文件
super.template = cfg.getTemplate(ftl);
}catch (IOException e){
e.printStackTrace();
}  
    }

    public void generateCode() throws Exception {
System.out.println("---------------开始生成代码");
valueMap.put("package","cn.itrip");//自定义包的位置
OutputStreamWriter writer=null;
for(TableVo table:tableList) {
valueMap.put("table",table);
try{
//创建一个写入器
writer = new FileWriter(savePath+"/"+table.getClassName()+".java");//根据不同的文件自行修改
//合成数据和模板,写入到文件
this.template.process(valueMap,writer);
writer.flush();
}catch (TemplateException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally {
writer.close();
}
}
System.out.println("生成代码成功!");
}
}

----------------模板文件(pojo.ftl)

package ${package}.pojo;

import java.io.Serializable;
import java.util.Date; /**
* ${table.comment}
*/
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>
}

---------------入口

package cn.itrip;

import cn.itrip.generator.EntityGenerator;public class App {
public static void main(String[] args)throws Exception{
System.out.println("开始启动---------------");
String path=App.class.getClassLoader().getResource("").getPath();
System.out.println(path);
EntityGenerator generator =new EntityGenerator("pojo.ftl");
try {
generator.setSavePath("D:\\object\\itripljh\\itripbeans\\src\\main\\java\\pojo2");
generator.generateCode();
}catch (Exception e){
e.printStackTrace();
} }

-----------最后执行的结果如下

完成。

代码生成器——实现生成pojo,sql,mapper接口的更多相关文章

  1. MyBatis-使用mybatis-generator-core.jar生成POJO和Mapper文件

    Demo: http://pan.baidu.com/s/1pLeyVv9 1.pom.xml <dependencies> <!-- 用于生成日志 --> <depen ...

  2. Mybatis逆向工程生成po、mapper接口、mapper.xml

    Mybatis逆向工程生成po.mapper接口.mapper.xml 一.新建一个maven工程 请查看我的另一篇博客:<使用idea创建一个maven工程> 二.引入所需依赖 需要my ...

  3. MyBatis逆向工程生成配置 generator (生成pojo、mapper.xml、mapper.java)

    MyBatis逆向工程生成 mybatis需要程序员自己编写sql语句,mybatis官方提供逆向工程,可以针对单表自动生成mybatis执行所需要的代码(mapper.java.mapper.xml ...

  4. mybatis如何根据mapper接口生成其实现类

    SpringBoot集成mybatis mybatis的statement的解析与加载 mybatis如何根据mapper接口生成其实现类 mybatis的mapper返回map结果集 mybatis ...

  5. mybatis如何根据mapper接口生成其实现类(springboot)

    序 mybatis里头给sqlSession指定执行哪条sql的时候,有两种方式,一种是写mapper的xml的namespace+statementId,如下: public Student fin ...

  6. Mybatis在IDEA中使用generator逆向工程生成pojo,mapper

    使用mybatis可以逆向生成pojo和mapper文件有很多种方式,我以前用的是mybtais自带的generator包来生成,连接如下:mybatis自己生成pojo 今天我用了IDEA上使用ma ...

  7. 使用maven的mybatis-generator代码生成器插件生成实体类、mapper配置文件和mapper接口(使用idea)

    接着之前创建的ssmMaven项目 一: 在pom文件中加入mybatis-generator插件 <plugins> <plugin> <groupId>org. ...

  8. 5.7 Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。-mybatis-generator将数据库表反向生成对应的实体类及基于mybatis的mapper接口和xml映射文件(类似代码生成器)

    一. liquibase 使用说明 功能概述:通过xml文件规范化维护数据库表结构及初始化数据. 1.配置不同环境下的数据库信息 (1)创建不同环境的数据库. (2)在resource/liquiba ...

  9. java web(七): mybatis的动态sql和mybatis generator自动生成pojo类和映射文件

    前言: MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据 不同条件拼接 SQL 语句的痛苦.例如拼接时要确保不能忘记添加必要的空格,还 ...

随机推荐

  1. Movavi Video Editor 15 Plus(视频编辑软件) 中文版

    Movavi Video Editor 15 Plus Mac版是Movavi系列中的一款视频编辑器,Movavi Video Editor Plus 15破解版提供了全面的视频功能,另外还支持为视频 ...

  2. abap 变量检查

    1:sap logon中可以对变量命名进行检查 program>check>code inspector

  3. Java 基础 多线程和线程池基础

    一,多线程 1.1 多线程介绍 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负 ...

  4. Javascript动态生成的页面信息爬取和openpyxl包FAQ小记

    最近,笔者在使用Requests模拟浏览器发送Post请求时,发现程序返回的html与浏览器F12观察到的略有不同,经过观察返回的response.text,cookies确认有效,因为我们可以看到返 ...

  5. Solaris环境下使用snoop命令抓包

    (1)报文抓取 Solaris中自带有snoop抓包工具,通过执行相应的命令抓取. 抓取目的地址为10.8.3.250的数据包,并存放到/opt/cap250的文件里 snoop -o /opt/ca ...

  6. C#6.0,C#7.0新特性

    C#6.0新特性 Auto-Property enhancements(自动属性增强) Read-only auto-properties (真正的只读属性) Auto-Property Initia ...

  7. [Chrome] chrome 自动跳转到https

    关键字眼: - static_upgrade_mode: FORCE_HTTPS - You cannot visit www.xxx.dev right now because the websit ...

  8. laravel application 容器app

    vendor/laravel/framework/src/Illuminate/Foundation/Application.php Application是laravel的核心容器,几乎处理所有东西 ...

  9. Linux机器重启情况查询

    在实际开发过程中,有时可能发现有一些服务器的进程挂了,查询相关错误日志也没有头绪.此时需要考虑是否是由于机器重启导致的错误 使用命令last reboot来查看是否机器自动重启 导致服务器重启的原因有 ...

  10. docker运行镜像报错:"write init-p: broken pipe"

    docker: Error response from daemon: OCI runtime create failed: container_linux.go:344: starting cont ...