逆向生成原理
逆向工程原理
- 前言
- 逆向工程的原理
- 1.Freemarker模板引擎
- 2.逆向工程的原理
前言
在我们实际开发过程中,开发流程大体可以分为需求分析、数据库字段设计、然后再开始编码,然后就开始创建我们实体类、controller、service、serviceImpl、mapper,进行CRUD的编写。虽然看起来这些操作都十分简单,但是会占据我们大量的开发时间,我们简单的CRUD编写已经有MyBatis和MyBatis-plus框架替我们完成。但是我们还能不能再减轻我们无关的工作量呢?类似于Springboot那样,将我们复杂的依赖交给容器管理。答案是可以的,我们研究不同的实体类、controller层不难发现,就拿实体类来举例,每个实体类不同的地方就是实体类属性的字段类型跟字段名不一样,而这些字段不就是我们从数据库映射过来的吗?那么我们只要能够从数据库那拿到字段信息再加以转换不就可以吗。这也就是我们MyBatis的逆向工程或者MyBatis-plus的逆向工程的原理,也是很多逆向工程的原理,如renrenfast、若依框架等等。但是哪些框架更多的在乎通用性,做了很多对我们没有用的封装。假如我们有一些定制的请求就很难进行业务逻辑的定制,那么就还是需要我们自己手动添加。所以我们需要理解逆向工程的原理。我们才能够知道在哪里修改才能出现我们自己想要的结果。接下来我就来讲解下逆向工程的原理。
逆向工程的原理
1.Freemarker模板引擎
在此之前,我们先来了解一下什么是Freemarker模板引擎?说通俗易懂一点就是我们制造一个模板,然后将数据往模板里面填入数据。类似于动态SQL和前端vue的做法。Freemarker的语法也比较简单。举几个简单的例子,更多不在这里展开,有需要的在网上搜索其他资料了解。
首先引入依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
例如我们在D盘ftl目录下有一个模板文件test.ftl,内容如下:
<h1>
public class ${pojoName} implements Serializable {
}
</h1>
然后我们后端向就需要往这个模板里面填入数据并输出为一个新文件,具体使用如下:
public class FreemarkerTest {
public static void main(String[] args) throws Exception {
// 第一步:创建一个Configuration对象,直接new一个对象。构造方法的参数就是freemarker对于的版本号。
Configuration configuration = new Configuration(Configuration.getVersion());
// 第二步:设置模板文件所在的路径。
configuration.setDirectoryForTemplateLoading(new File("D:\\ftl"));
// 第三步:设置模板文件使用的字符集。一般就是utf‐8. 注意版本。新版本不需要
configuration.setDefaultEncoding("UTF‐8");
// 第四步:加载一个模板,创建一个模板对象。
Template template = configuration.getTemplate("test.ftl");
// 第五步:创建一个模板使用的数据集,可以是pojo也可以是map。一般是Map。
Map dataModel = new HashMap<>();
//向数据集中添加数据
dataModel.put("pojoName", "user");
// 第六步:创建一个Writer对象,一般创建一FileWriter对象,指定生成的文件名。
Writer out = new FileWriter(new File("D:\\ftl\\out\\test.html"));
// 第七步:调用模板对象的process方法输出文件。
template.process(dataModel, out);
// 第八步:关闭流。
out.close();
}
}
然后打开test.html文件就可以看到下图内容
可以看到模板引擎自动将我们传入的数据填充到模板中,并成功展现。
到此,恭喜你,你已经会一些简单的Freemarker操作了。
一个ftl模版文件,是由少数几个动态标签加上其他静态的内容组成。动态标签包含以下几种:
- 普通参数
${demo}
- list标签
<#list studentList as student>
`student.id/{student.id}/student.id/`{[studnet.name](http://studnet.name/)}
</#list>
- if条件标签
<#if student_index % 2 == 0>
1
<#else>
0
</#if>
- 在if标签中,还可以进行简单的null值判断
<#if a??>
a不为空时。。
<#else>
a为空时###
</#if>
- 日期标签
当前日期: `date?date
当前时间:{date?date}
当前时间:date?date
当前时间:`{date?time}
当前日期和时间:`date?datetime
自定义日期格式:{date?datetime}
自定义日期格式:date?datetime
自定义日期格式:`{date?string("yyyyMM/dd HH:mm: ss")}
- 包含标签
<#include "test.ftl"/>
以上只是为了后面讲解时,不至于看不懂,略微讲解一下,如果感兴趣,可以去网上搜索相关教程进行学习。
2.逆向工程的原理
接下来我们来看一看MyBatis-plus的逆向工程是怎么实现的。
(1)引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.0.5</version>
</dependency>
查看文件,我们不难发现里面就是一堆ftl文件,结果显而易见,最终都是查出相关数据,然后将数据添加到相关的模板代码之中,然后得到最终的文件,最后我们只需要了解我们所需要的数据是怎么得到的。我们就能够清楚逆向工程的原理。
通常,我们使用jdbc只是用来查询数据,但是我们也可以用来查询表的结构以及注释信息,下面示例代码就是获取我们用来逆向生成的数据。得到数据填入模板中就能得到最终的文件。
public static void main(String[] args) throws Exception {
//mysql
Class.forName("com.mysql.cj.jdbc.Driver");
Properties props = new Properties();
props.put("useInformationSchema", "true"); //mysql获取表注释需要加上这个属性
props.put("user", "root");
props.put("password", "root");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/genserver?serverTimezone=GMT%2B8&characterEncoding=utf-8&autoReconnect=true",props);
//oracle
// Class.forName("oracle.jdbc.driver.OracleDriver");
// Connection con = DriverManager.getConnection("jdbc:oracle:thin:@172.16.49.65:1521/motion","swordrisk","risk#230");
// Properties props = new Properties();
// props.put("remarksReporting", "true"); //要获取注释,需要增加这个属性。
// props.put("user", "swordrisk");
// props.put("password", "risk#230");
// Connection con = DriverManager.getConnection("jdbc:oracle:thin:@172.16.49.65:1521/motion",props);
//上面建立连接
System.out.println("========映射表信息==============");
DatabaseMetaData meta = con.getMetaData();
ResultSet tables = meta.getTables("genserver", "%", "black_info", new String[] {"TABLE"});
while(tables.next()) {
ResultSetMetaData metaData = tables.getMetaData();
System.out.println(metaData.getColumnCount());
for(int i = 1 ; i <= metaData.getColumnCount(); i ++) {
System.out.println(metaData.getColumnName(i)+" ==> "+tables.getString(metaData.getColumnName(i)));
}
System.out.println(tables.getString("TABLE_NAME")+" --->>> "+tables.getString("REMARKS"));
}
System.out.println("========映射列信息==============");
ResultSet columns = meta.getColumns("genserver", "%", "black_info", "%");
System.out.println("columnName|columnType|datasize|digits|nullable|remarks");
while(columns.next()) {
String columnName = columns.getString("COLUMN_NAME");
String columnType = columns.getString("TYPE_NAME");
int datasize = columns.getInt("COLUMN_SIZE");
int digits = columns.getInt("DECIMAL_DIGITS");
int nullable = columns.getInt("NULLABLE");
String remarks = columns.getString("REMARKS");
System.out.println(columnName+"|"+columnType+"|"+datasize+"|"+digits+"|"+ nullable+"|"+remarks);
}
System.out.println("========映射主键信息==============");
ResultSet primaryKeys = meta.getPrimaryKeys("genserver", "%", "black_info");
while(primaryKeys.next()) {
ResultSetMetaData metaData = primaryKeys.getMetaData();
System.out.println("主键个数:"+metaData.getColumnCount());
for(int i = 1 ; i <= metaData.getColumnCount(); i ++) {
System.out.println(metaData.getColumnName(i)+" ==> "+primaryKeys.getString(metaData.getColumnName(i)));
}
}
}
输出结果
下面就是实体类的模板文件代码,感兴趣可以阅读一下。
package pojo;
import java.io.Serializable;
import java.util.Date;
import java.sql.Timestamp;
import com.alibaba.fastjson.annotation.JSONField;
import annotation.Table;
import annotation.TableColumn;
/**
* @desc:the module of table ${table_name}
* @desc for the condition of one page mamage the CRUD function of one table,
* you dan use the @Table and the @TableColumn annotation for rapidly coding
<#if table_remark !="">* @desc found table comment ${table_remark},default used as the fileName of the excel file Exported</#if>
* @author kklmars
* @date ${gen_time}
*/
@Table(tableName = "${table_name_small}"<#if table_remark !="">,expFileName="${table_remark}"<#else>,expFileName="导出数据文件"</#if> <#if table_order_by?exists>,orderBy="${table_order_by}"</#if><#if table_order?exists>,order="${table_order}"</#if>)
public class ${pojo_name} implements Serializable{
<#if COLUMNS?exists>
<#list COLUMNS as model>
/**
* this method is generated by GenUI.CodeGen.enjoy coding.
* ${model.javaName} the value of ${table_name}.${model.columnName}
* Found comment on this column is ${model.remarks} default used as the header of this column in excel exported and the query table on page
*/
<#if model.javaType=="Date">@JSONField(format="yyyy-MM-dd")
<#elseif model.javaType=="Timestamp">@JSONField(format="yyyy-MM-dd hh:mm:ss")</#if>
@TableColumn(dbColumn = "${model.columnName}",dbColumnType = "${model.columnType}"<#if model.remarks != "">,excelHeader = "${model.remarks}"</#if><#if model.isPK == "true">,isPK="true"</#if>)
private ${model.javaType} ${model.javaName};
</#list>
</#if>
private static final long serialVersionUID = 1L;
<#if COLUMNS?exists>
<#list COLUMNS as model>
public ${model.javaType} ${model.getterName}() {
return ${model.javaName};
}
public void ${model.setterName}(${model.javaType} ${model.javaName}) {
<#if model.javaType == "String">
this.${model.javaName} = ${model.javaName} == null ? null : ${model.javaName}.trim();
<#else>
this.${model.javaName} = ${model.javaName};
</#if>
}
</#list>
</#if>
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("pojo.${pojo_name} ").append("[");
<#if COLUMNS?exists>
<#list COLUMNS as model>
sb.append(", ${model.javaName} = ").append(${model.javaName});
</#list>
</#if>
sb.append("]");
return sb.toString();
}
}
综上,我们就理清了逆向工程的原理,就是将实体类、controller、service、serviceImpl、mapper等里面不会发生变化的作为静态模板,然后我们通过jdbc建立连接,在连接中获取表信息。然后填充到模板中,最终就能得到最终的实体类或我们想要的文件。当然,这种方法不仅仅只能用在这个地方,在我们热点商品详情页、首页等等不会发生太大变化、但是访问非常高的地方,我们就可以利用模板生成静态页面,然后放入nginx中,提高我们的系统性能。
最后希望能够点赞关注收藏,让我创作更多有意义的文章。