autoCode是一个代码生成器项目,基于velocity模板引擎,采用SpringMVC + mybatis + jquery easyUI.
其特点主要有:
- 用户登陆 - 每个用户有他自己独立的数据库连接配置和模板配置
- 数据源配置 - 可以配置多个数据源,多种数据库类型(目前支持Mysql,MSServer数据库,可以扩展)
- 模板配置 - 定义自己的模板,采用velocity模板语法.这样可以根据模板来生成不同的代码,如POJO,Dao,mybatis配置文件等
- 操作简单 - 生成代码只需三步:1. 选择数据源;2. 选择表;3. 选择模板.
页面截图:
首页
配置数据源
配置模板
修改模板:
生成代码第一步:
生成代码第二步:
生成代码第三步:
生成结果,点击左边的树菜单可以查看内容:
下载地址:http://pan.baidu.com/share/link?shareid=3882948706&uk=4114964897
1. 解压压缩包,eclipse正常导入工程(import... -> Existing Projects into Workspace..)
2. 导入数据库(MYSQL),SQL文件在项目根目录下,名为autoCode.sql,运行里面的内容即可
3. 修改数据库连接参数,配置文件在src/main/resources/server.properties
4. 启动项目
登录用户名密码均为admin
首次使用
第一步添加数据源:
输入数据库连接参数
保存后测试连接
第二步:生成代码
选择一个数据源
选择一张表,这里为了演示,我们用代码生成器自带的用户表
选择模板,这里系统自带了几个模板
生成后,点击树状菜单查看模板内容
大家也可以自行创建自己的模板,模板参数在界面右边已经给出
也可以仿照现有的模板自己琢磨下 O(∩_∩)O
这里还提供了用户管理,可以开几个账号给其他小伙伴使用,各自使用自己的数据源,互不干扰.
autoCode代码生成系统设计思路
先来看下一个普通的pojo类由哪几部分组成
- 类名(表名)
- 属性名(字段名)
- 属性类型(字段类型)
因此,只要我们想办法获取到表结构的一些信息,然后把这些信息拼装成对应的java类信息即可.
就Mysql数据库来说,获取一个数据库中的表名的SQL是:SHOW TABLES
如图所示,这里我查询了autoCode自己的数据库
数据库表名获取到了,接着我们应该获取一张表里面的所有字段信息,mysql对应的SQL是:DESC 表名
如图所示,我们查询了datasource_config表的信息.其中字段名,字段类型,主键,自增都可以得到
我们只要把这些信息放在对应的java类里面就可以使用了.系统对应的是ColumnDefinition类
一个类代表一个字段的信息,因此所有字段的信息我们应该用List来保存
我们得到了表名以及字段信息,现在要做的是把这些信息放到velocity上下文中去
然后通过模板来来生成代码
- private String doGenerator(SQLContext sqlContext,String template){
- VelocityContext context = new VelocityContext();
-
- context.put("context", sqlContext);
- context.put("table", sqlContext.getTableDefinition());
- context.put("pkColumn", sqlContext.getTableDefinition().getPkColumn());
- context.put("columns", sqlContext.getTableDefinition().getColumnDefinitions());
-
- return VelocityUtil.generate(context, template);
- }
这里是GeneratorService类中的一个方法,SQLContext中存放了表名,字段信息
然后安装事先约定好的velocity语法生成内容.String template是模板内容
代码生成完后直接返回到前台页面即可
autoCode类设计
由于代码生成系统需要支持多种数据库,因此在类的设计上需要仔细得思考下,把相同的部分抽取出来,不同的部分通过接口或抽象类来扩展.
先来看下不同的地方
- 获取数据库中所有的表
- 获取表中的字段信息
再来看相同的地方:
- 返回表名集合
- 返回SQL上下文集合(SQL上下文中已经封装好了表名,字段等信息)
针对于相同点和不同点,我分别用了两个抽象来来实现
如果所示,getShowTablesSQL()返回的是查询所有表名的SQL,getTableDetailSQL()返回具体表信息的SQL,buildColumnDefinition()是构建字段信息.这三个方法需要子类去实现.
getTableList()是抽象类中已经实现好的方法,用来返回表名集合
同样buildSQLContextList()也是抽象类中实现好的方法,用来构建SQL上下文,SQL上下文中已经封装好了表名,字段等信息
我们需要用到的也只有这两个方法而已
以后要添加其它数据库类型,只需要继承者两个类即可
下面贴上用户操作序列图
最后贴上系统生成代码的序列图
- 用户请求到Controller类
- Controller类调用Service中的generate()方法
- Service通过工厂类构建出对应的数据库类型SQLService,这个SQLService决定了使用哪种数据库
- 接着SQLService获取TableSelector
- TableSelector通过表名构建SQLContext上下文
- 遍历SQLContext上下文,通过velocity模板生成代码内容
autoCode代码生成器,velocity变量详解
autoCode提供了一组velocity变量,这些变量包含了数据库表,字段,java类字段信息
运用这些变量我们就能构建出一个自己需要的模板文件
首先来看下变量列表:
前面是变量名,后面是对变量的说明
接下来我们就用这些变量来编写一个模板
这里有一张后台用户表back_user,它有三个字段,如图
现在我们要根据这张表生成一个Entity,那模板内容如下:
假设包名为test
- package ${context.packageName}.entity;
-
- public class ${context.javaBeanName} {
- #foreach($column in $columns)
- private ${column.javaType} ${column.javaFieldName};
- #end
-
- #foreach(${column} in ${columns})
- public void set${column.javaFieldNameUF}(${column.javaType} ${column.javaFieldName}){
- this.${column.javaFieldName} = ${column.javaFieldName};
- }
-
- public ${column.javaType} get${column.javaFieldNameUF}(){
- return this.${column.javaFieldName};
- }
-
- #end
- }
使用代码生成器生成后的代码如下:
- package test.entity;
-
- public class BackUser {
- private String username;
- private String password;
- private Date addTime;
-
- public void setUsername(String username){
- this.username = username;
- }
-
- public String getUsername(){
- return this.username;
- }
-
- public void setPassword(String password){
- this.password = password;
- }
-
- public String getPassword(){
- return this.password;
- }
-
- public void setAddTime(Date addTime){
- this.addTime = addTime;
- }
-
- public Date getAddTime(){
- return this.addTime;
- }
-
- }
接下来我们编写一个复杂的例子,编写一个mybatis的配置文件模板
- xml version="1.0" encoding="UTF-8" ?>
- >
- <mapper namespace="${context.packageName}.dao.${context.javaBeanName}Dao">
- <resultMap id="queryResultMap" type="${context.packageName}.entity.${context.javaBeanName}">
- #foreach($column in $columns)
- <result column="${column.columnName}" property="${column.javaFieldName}" jdbcType="${column.mybatisJdbcType}" />
- #end
- resultMap>
-
- <select id="find" parameterType="${context.packageName}.entity.${context.javaBeanName}"
- resultMap="queryResultMap">
- SELECT *
- FROM ${table.tableName} t
- WHERE 1=1
- #foreach($column in $columns)
- <if test="${column.javaFieldName}!=null">
- AND ${column.columnName} = #{${column.javaFieldName},jdbcType=${column.mybatisJdbcType}}
- if>
- #end
- ORDER BY ${context.javaPkName} desc
-
- select>
-
-
- <insert id="save" parameterType="${context.packageName}.entity.${context.javaBeanName}"
- #if(${pkColumn.isIdentity})
- keyProperty="${context.javaPkName}" keyColumn="${context.pkName}" useGeneratedKeys="true"
- #end
- >
- INSERT INTO ${table.tableName}
- (
- #set ($i=0)
- #foreach($column in $columns)
- #if(!${column.isIdentityPk})
- #if($i > 0),#end ${column.columnName}
- #set($i=$i+1)
- #end
- #end
- )
- VALUES (
- #set ($i=0)
- #foreach($column in $columns)
- #if(!${column.isIdentityPk})
- #if($i > 0),#end #{${column.javaFieldName},jdbcType=${column.mybatisJdbcType}}
- #set($i=$i+1)
- #end
- #end
-
- )
- insert>
-
-
- <update id="update" parameterType="${context.packageName}.entity.${context.javaBeanName}">
- UPDATE ${table.tableName}
- SET
- #set ($i=0)
- #foreach($column in $columns)
- #if(!${column.isPk})
- #if($i > 0),#end ${column.columnName}=#{${column.javaFieldName},jdbcType=${column.mybatisJdbcType}}
- #set($i=$i+1)
- #end
- #end
- WHERE ${context.pkName} = #{${context.javaPkName},jdbcType=${context.mybatisPkType}}
- update>
-
- <select id="get" resultMap="queryResultMap" parameterType="java.io.Serializable">
- SELECT *
- FROM ${table.tableName}
- WHERE ${context.pkName} = #{${context.javaPkName},jdbcType=${context.mybatisPkType}}
- select>
-
- <delete id="del" parameterType="${context.packageName}.entity.${context.javaBeanName}">
- DELETE FROM ${table.tableName}
- WHERE ${context.pkName} = #{${context.javaPkName},jdbcType=${context.mybatisPkType}}
- delete>
-
- mapper>
生成后的代码:
- xml version="1.0" encoding="UTF-8" ?>
- >
- <mapper namespace="test.dao.BackUserDao">
- <resultMap id="queryResultMap" type="test.entity.BackUser">
- <result column="username" property="username" jdbcType="VARCHAR" />
- <result column="password" property="password" jdbcType="VARCHAR" />
- <result column="add_time" property="addTime" jdbcType="TIMESTAMP" />
- resultMap>
-
- <select id="find" parameterType="test.entity.BackUser"
- resultMap="queryResultMap">
- SELECT *
- FROM back_user t
- WHERE 1=1
- <if test="username!=null">
- AND username = #{username,jdbcType=VARCHAR}
- if>
- <if test="password!=null">
- AND password = #{password,jdbcType=VARCHAR}
- if>
- <if test="addTime!=null">
- AND add_time = #{addTime,jdbcType=TIMESTAMP}
- if>
- ORDER BY username desc
-
- select>
-
-
- <insert id="save" parameterType="test.entity.BackUser">
- INSERT INTO back_user
- (
- username
- , password
- , add_time
- )
- VALUES (
- #{username,jdbcType=VARCHAR}
- , #{password,jdbcType=VARCHAR}
- , #{addTime,jdbcType=TIMESTAMP}
-
- )
- insert>
-
-
- <update id="update" parameterType="test.entity.BackUser">
- UPDATE back_user
- SET
- password=#{password,jdbcType=VARCHAR}
- , add_time=#{addTime,jdbcType=TIMESTAMP}
- WHERE username = #{username,jdbcType=VARCHAR}
- update>
-
- <select id="get" resultMap="queryResultMap" parameterType="java.io.Serializable">
- SELECT *
- FROM back_user
- WHERE username = #{username,jdbcType=VARCHAR}
- select>
-
- <delete id="del" parameterType="test.entity.BackUser">
- DELETE FROM back_user
- WHERE username = #{username,jdbcType=VARCHAR}
- delete>
-
- mapper>
我们取一个update语句来分析:
UPDATE ${table.tableName} // 表名
SET
#set ($i=0) // 定义一个变量$i=0
#foreach($column in $columns) // 循环列
#if(!${column.isPk}) // 如果不是主键,因为主键是不能被修改的
#if($i > 0),#end // 这里添加逗号,从第二个开始就开始加逗号
${column.columnName}=#{${column.javaFieldName},jdbcType=${column.mybatisJdbcType}} // 修改字段
#set($i=$i+1) // 每次循环后变量加1
#end
#end
WHERE ${context.pkName} = #{${context.javaPkName},jdbcType=${context.mybatisPkType}} // 主键=#{java主键}
由此可以看出这套velocity语法可以满足大部分要求,当然特殊的地方还是需要自己去修改.毕竟代码生成器也不是外能的