若依-代码自动生成解析

源码阅读技巧

  • 可以从前端开始,前端的url请求暗含了业务流程,借助F12(打开浏览器控制台)后的network可以更快的找到对API的请求
  • 根据url对后端项目进行搜索,使用ctrl+shift+f打开全局搜索,例如搜索“/login”,可以查询出项目包含/login的文本

代码自动生成功能

会生成 前端,后端,数据库sql,三个模块的代码

使用

  • 在数据库建一张以sys_开头的表
  • 管理系统中选择系统工具—代码生成—导入—生成—下载代码
  • 将下载得到的代码拷贝到自己的项目目录中
  • 执行生成的sql文件,获得的代码包含三个模块,前端,后端,数据库sql,前面两个只要拷贝,sql文件需要手动执行
  • 可以修改sys_menu这张表下的parent_id,使得menu处于你想要的位置,自动生成的id一般为3

sql解析

我这里生成的表为sys_goods,字段为goods_id,goods_name_goods_price,goods_status

自动生成的goodsMenu.sql由三个部分组成,具体如下

-- 菜单 SQL
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('货物信息', '3', '1', 'goods', 'system/goods/index', 1, 0, 'C', '0', '0', 'system:goods:list', '#', 'admin', sysdate(), '', null, '货物信息菜单');
  • ‘货物信息’ : 字段名字

  • ‘3’ : 父id为3(父id后来被手动改成1,不然这条menu会在系统工具那里显示,而不是在系统管理那里显示)

  • ‘1’ : 显示顺序为1

  • ‘goods’ : 路由地址为“goods”

  • ‘system/goods/index’ : 组件地址为’system/goods/index’(这里感觉有点问题,整个项目里并未使用过system/goods/index,也没拼接过该地址,但不影响界面显示和代码运行)

  • 1 : 不是外链

  • 0 : 内容进行缓存

  • ‘C’ : 类型为菜单

  • ‘0’ , ‘0’ : 显示菜单,且菜单状态正常

  • ‘system:goods:list’,权限标志,在PermissionService类下的hasPermi(String permission)方法会使用到这个字段,从当前登录的用户中取出用户权限List,判断当前请求所需的permission是否在这个list中

  • ‘#’ : 菜单icon,它回去找ruoyi-ui/src/assets/icons/svg/xxx.svg,将这个svg图标显示到menu的左边,没有改图标则为空

  • ‘admin’ : 创建这条记录的人是’admin’

  • sysdate() : 返回现在时间的一个函数,返回格式为“yyyy-MM-dd HH:mm:ss”

  • ‘’ : 修改人

  • null : 修改时间为空

  • ‘货物信息菜单’ : 备注为’货物信息菜单’

-- 按钮父菜单ID
SELECT @parentId := LAST_INSERT_ID();
  • 设置一个变量是 @parentId
  • :=是赋值
  • LAST_INSERT_ID() : 返回最后一个 INSERT 或 UPDATE 操作为 AUTO_INCREMENT 列设置的最新发生的值.
    LAST_INSERT_ID是基于单个connection的, 不可能被其它的客户端连接改变,同时这个函数跟表无关联。

-- 按钮 SQL
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('货物信息查询', @parentId, '1',  '#', '', 1, 0, 'F', '0', '0', 'system:goods:query',        '#', 'admin', sysdate(), '', null, '');

insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('货物信息新增', @parentId, '2',  '#', '', 1, 0, 'F', '0', '0', 'system:goods:add',          '#', 'admin', sysdate(), '', null, '');

insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('货物信息修改', @parentId, '3',  '#', '', 1, 0, 'F', '0', '0', 'system:goods:edit',         '#', 'admin', sysdate(), '', null, '');

insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('货物信息删除', @parentId, '4',  '#', '', 1, 0, 'F', '0', '0', 'system:goods:remove',       '#', 'admin', sysdate(), '', null, '');

insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('货物信息导出', @parentId, '5',  '#', '', 1, 0, 'F', '0', '0', 'system:goods:export',       '#', 'admin', sysdate(), '', null, '');

整体思路和第一部分菜单 SQL差距不大,注意下@parentId是之前定义的变量,'F’是指按钮

前端文件

goods.js
import request from '@/utils/request'

导入ruoyi用于发送请求的axios实例

// 查询货物信息列表
export function listGoods(query) {
  return request({
    url: '/system/goods/list',
    method: 'get',
    params: query
  })
}

发送请求,url为’/system/goods/list’,类型为get,参数由调用方传递


// 查询货物信息详细
export function getGoods(goodsId) {
  return request({
    url: '/system/goods/' + goodsId,
    method: 'get'
  })
}

// 新增货物信息
export function addGoods(data) {
  return request({
    url: '/system/goods',
    method: 'post',
    data: data
  })
}

// 修改货物信息
export function updateGoods(data) {
  return request({
    url: '/system/goods',
    method: 'put',
    data: data
  })
}

// 删除货物信息
export function delGoods(goodsId) {
  return request({
    url: '/system/goods/' + goodsId,
    method: 'delete'
  })
}

// 导出货物信息
export function exportGoods(query) {
  return request({
    url: '/system/goods/export',
    method: 'get',
    params: query
  })
}

剩下的CRUD操作,和listGoods(query) ,没有本质区别

index.vue

主要由template和script两个部分组成

整体样式:

在这里插入图片描述

template 部分:

头部表单(el-form):

在这里插入图片描述

      <el-form-item label="名称" prop="goodsName">
        <el-input
          v-model="queryParams.goodsName"
          placeholder="请输入名称"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
  • el-form-item : 表示这是element-ui下的一个标签,是一个item
  • el-input : element-ui的输入框
  • clearable:表示清空内容
  • @keyup.enter.native : 表示对回车事件的处理,后面跟处理的方法名字

后面还跟着类似内容的价格和状态的item,解释如上

再之后则是一组button

<el-form-item>
  <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
  • handleQuery:和前面处理回车时间的方法一样
  • resetQuery:由两个部分组成,一个是resetForm,还有一个是handleQuery,第一个是为了清空内容,第二个用来刷新内容

四个功能按键:

在这里插入图片描述

<el-row :gutter="10" class="mb8">
  <el-col :span="1.5">
    <el-button
      type="primary"
      plain
      icon="el-icon-plus"
      size="mini"
      @click="handleAdd"
      v-hasPermi="['system:goods:add']"
    >新增</el-button>
  </el-col>
    ...
</el-row>
  • gutter:gutter是指栅格间间隔,也可以使用offset,offset是指栅格左侧的间隔格数

  • v-hasPermi : ruoyi封装的一个指令权限,能简单快速的实现按钮级别的权限判断。v-permission

  • 后面的修改、删除、导出原理类似,有个:disabled=“multiple”,是用来设置禁用情况的

el-table:

在这里插入图片描述

<el-table v-loading="loading" :data="goodsList" @selection-change="handleSelectionChange">
  <el-table-column type="selection" width="55" align="center" />
  <el-table-column label="编号" align="center" prop="goodsId" />
  <el-table-column label="名称" align="center" prop="goodsName" />
  <el-table-column label="价格" align="center" prop="goodsPrice" />
  <el-table-column label="状态" align="center" prop="goodsStatus" />
  <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
    <template slot-scope="scope">
      <el-button
        size="mini"
        type="text"
        icon="el-icon-edit"
        @click="handleUpdate(scope.row)"
        v-hasPermi="['system:goods:edit']"
      >修改</el-button>
      <el-button
        size="mini"
        type="text"
        icon="el-icon-delete"
        @click="handleDelete(scope.row)"
        v-hasPermi="['system:goods:remove']"
      >删除</el-button>
    </template>
  </el-table-column>
</el-table>

script部分:

import { listGoods, getGoods, delGoods, addGoods, updateGoods, exportGoods } from "@/api/system/goods";

先从goods.js中导入方法

之后定义data()数据区,created()页面初始化前做的准备,methods方法区:

data区主要定义了一些常量

data() {
  return {
    // 遮罩层
    loading: true,
    // 选中数组
    ids: [],
    // 非单个禁用
    single: true,
    // 非多个禁用
    multiple: true,
    // 显示搜索条件
    showSearch: true,
    // 总条数
    total: 0,
    // 货物信息表格数据
    goodsList: [],
    // 弹出层标题
    title: "",
    // 是否显示弹出层
    open: false,
    // 查询参数
    queryParams: {
      pageNum: 1,
      pageSize: 10,
      goodsName: null,
      goodsPrice: null,
      goodsStatus: null
    },
    // 表单参数
    form: {},
    // 表单校验
    rules: {
    }
  };
},

create(),调用了后面方法区methods中定义的方法

created() {
  this.getList();
}

methods

methods: {
  /** 查询货物信息列表 */
  getList() {
    this.loading = true;
    listGoods(this.queryParams).then(response => {
      this.goodsList = response.rows;
      this.total = response.total;
      this.loading = false;
    });
  },
  // 取消按钮
  cancel() {
    this.open = false;
    this.reset();
  },
  // 表单重置
  reset() {
    this.form = {
      goodsId: null,
      goodsName: null,
      goodsPrice: null,
      goodsStatus: "0"
    };
    this.resetForm("form");
  },
  /** 搜索按钮操作 */
  handleQuery() {
    this.queryParams.pageNum = 1;
    this.getList();
  },
  /** 重置按钮操作 */
  resetQuery() {
    this.resetForm("queryForm");
    this.handleQuery();
  },
  // 多选框选中数据
  handleSelectionChange(selection) {
    this.ids = selection.map(item => item.goodsId)
    this.single = selection.length!==1
    this.multiple = !selection.length
  },
  /** 新增按钮操作 */
  handleAdd() {
    this.reset();
    this.open = true;
    this.title = "添加货物信息";
  },
  /** 修改按钮操作 */
  handleUpdate(row) {
    this.reset();
    const goodsId = row.goodsId || this.ids
    getGoods(goodsId).then(response => {
      this.form = response.data;
      this.open = true;
      this.title = "修改货物信息";
    });
  },
  /** 提交按钮 */
  submitForm() {
    this.$refs["form"].validate(valid => {
      if (valid) {
        if (this.form.goodsId != null) {
          updateGoods(this.form).then(response => {
            this.msgSuccess("修改成功");
            this.open = false;
            this.getList();
          });
        } else {
          addGoods(this.form).then(response => {
            this.msgSuccess("新增成功");
            this.open = false;
            this.getList();
          });
        }
      }
    });
  },
  /** 删除按钮操作 */
  handleDelete(row) {
    const goodsIds = row.goodsId || this.ids;
    this.$confirm('是否确认删除货物信息编号为"' + goodsIds + '"的数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function() {
        return delGoods(goodsIds);
      }).then(() => {
        this.getList();
        this.msgSuccess("删除成功");
      })
  },
  /** 导出按钮操作 */
  handleExport() {
    const queryParams = this.queryParams;
    this.$confirm('是否确认导出所有货物信息数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function() {
        return exportGoods(queryParams);
      }).then(response => {
        this.download(response.msg);
      })
  }
}

后端文件

SysGoods 实体类
public class SysGoods extends BaseEntity
{
    private static final long serialVersionUID = 1L;
    /** 编号 */
    private Long goodsId;

    /** 名称 */
    @Excel(name = "名称")
    private String goodsName;

    /** 价格 */
    @Excel(name = "价格")
    private Integer goodsPrice;

    /** 状态(0正常 1不足) */
    @Excel(name = "状态", readConverterExp = "0=正常,1=不足")
    private String goodsStatus;

    //。。。省略get和set方法
    
    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("goodsId", getGoodsId())
            .append("goodsName", getGoodsName())
            .append("goodsPrice", getGoodsPrice())
            .append("goodsStatus", getGoodsStatus())
            .toString();
    }
}
  • BaseEntity中主要是一些更新时间、创建者等公共而基础的属性

  • @Excel(name = “状态”, readConverterExp = “0=正常,1=不足”),导出到Excel的名字为状态,readConverterExp内容转换表达式,读入0,会转换为正常

  • ToStringBuilder,apache的commons-lang3的工具包里有一个ToStringBuilder类,使用IDEA自动生成的toString方法中,会使用“+”来拼接字符串,每次“+”都是在new一个新的对象,这比较消耗内存,使用ToStringBuilder可以减少内存消耗

SysGoodsController
@RestController//@Controller+@ResponseBody两个注解的结合,返回json数据
@RequestMapping("/system/goods")//映射路由
public class SysGoodsController extends BaseController
{
    @Autowired
    private ISysGoodsService sysGoodsService;

    /**
     * 查询货物信息列表
     */
    @PreAuthorize("@ss.hasPermi('system:goods:list')")//检查当前用户是否拥有相关权限
    @GetMapping("/list")
    public TableDataInfo list(SysGoods sysGoods)
    {
        startPage();
        List<SysGoods> list = sysGoodsService.selectSysGoodsList(sysGoods);
        return getDataTable(list);
    }

    /**
     * 导出货物信息列表
     */
    @PreAuthorize("@ss.hasPermi('system:goods:export')")
    @Log(title = "货物信息", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(SysGoods sysGoods)
    {
        List<SysGoods> list = sysGoodsService.selectSysGoodsList(sysGoods);
        ExcelUtil<SysGoods> util = new ExcelUtil<SysGoods>(SysGoods.class);
        return util.exportExcel(list, "货物信息数据");//对list数据源将其里面的数据导入到excel表单
    }

    /**
     * 获取货物信息详细信息
     */
    @PreAuthorize("@ss.hasPermi('system:goods:query')")
    @GetMapping(value = "/{goodsId}")
    public AjaxResult getInfo(@PathVariable("goodsId") Long goodsId)//AjaxResult一个继承了HashMap的子类
    {
        return AjaxResult.success(sysGoodsService.selectSysGoodsById(goodsId));//实际上就返回了一个map,key为“success”,value为null
    }

    /**
     * 新增货物信息
     */
    @PreAuthorize("@ss.hasPermi('system:goods:add')")
    @Log(title = "货物信息", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody SysGoods sysGoods)
    {
        return toAjax(sysGoodsService.insertSysGoods(sysGoods));//toAjax响应返回结果,影响行数大于0则返回success,否则为false
    }

    /**
     * 修改货物信息
     */
    @PreAuthorize("@ss.hasPermi('system:goods:edit')")
    @Log(title = "货物信息", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody SysGoods sysGoods)
    {
        return toAjax(sysGoodsService.updateSysGoods(sysGoods));
    }

    /**
     * 删除货物信息
     */
    @PreAuthorize("@ss.hasPermi('system:goods:remove')")
    @Log(title = "货物信息", businessType = BusinessType.DELETE)
   @DeleteMapping("/{goodsIds}")
    public AjaxResult remove(@PathVariable Long[] goodsIds)
    {
        return toAjax(sysGoodsService.deleteSysGoodsByIds(goodsIds));
    }
}

SysGoodsService层只是简单的调用,dao层只是简单声明了方法,故不作详细解释

SysGoodsMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- dtd文件,Document Type Definition,用来规则定义文档规则的,用来验证下面的节点是否符合。mapper表示根节点   -->
<mapper namespace="com.ruoyi.system.mapper.SysGoodsMapper">
   <!--  声明dao层的那个接口的全类名  -->
    <resultMap type="SysGoods" id="SysGoodsResult">
        <!-- 声明JavaBean和数据库表字段的映射关系   -->
        <result property="goodsId"    column="goods_id"    />
        <result property="goodsName"    column="goods_name"    />
        <result property="goodsPrice"    column="goods_price"    />
        <result property="goodsStatus"    column="goods_status"    />
    </resultMap>

    <sql id="selectSysGoodsVo">
        select goods_id, goods_name, goods_price, goods_status from sys_goods
    </sql>

    <select id="selectSysGoodsList" parameterType="SysGoods" resultMap="SysGoodsResult">
        <!--  selectSysGoodsList 和方法名一样,parameterType,传入的参数的类型,resultMap封装规则  -->
        <include refid="selectSysGoodsVo"/>
        <!--  复用sql  -->
        <where>  
            <if test="goodsName != null  and goodsName != ''"> and goods_name like concat('%', #{goodsName}, '%')</if>
            <!-- 动态拼接sql   -->
            <if test="goodsPrice != null "> and goods_price = #{goodsPrice}</if>
            <if test="goodsStatus != null  and goodsStatus != ''"> and goods_status = #{goodsStatus}</if>
        </where>
    </select>
    
    <select id="selectSysGoodsById" parameterType="Long" resultMap="SysGoodsResult">
        <include refid="selectSysGoodsVo"/>
        where goods_id = #{goodsId}
    </select>
        
    <insert id="insertSysGoods" parameterType="SysGoods" useGeneratedKeys="true" keyProperty="goodsId">
        insert into sys_goods
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <!-- suffixOverrides去除后缀 -->
            
            <if test="goodsName != null">goods_name,</if>
            <if test="goodsPrice != null">goods_price,</if>
            <if test="goodsStatus != null">goods_status,</if>
         </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="goodsName != null">#{goodsName},</if>
            <if test="goodsPrice != null">#{goodsPrice},</if>
            <if test="goodsStatus != null">#{goodsStatus},</if>
         </trim>
    </insert>

    <update id="updateSysGoods" parameterType="SysGoods">
        update sys_goods
        <trim prefix="SET" suffixOverrides=",">
            <if test="goodsName != null">goods_name = #{goodsName},</if>
            <if test="goodsPrice != null">goods_price = #{goodsPrice},</if>
            <if test="goodsStatus != null">goods_status = #{goodsStatus},</if>
        </trim>
        where goods_id = #{goodsId}
    </update>

    <delete id="deleteSysGoodsById" parameterType="Long">
        delete from sys_goods where goods_id = #{goodsId}
    </delete>

    <delete id="deleteSysGoodsByIds" parameterType="String">
        delete from sys_goods where goods_id in 
        <foreach item="goodsId" collection="array" open="(" separator="," close=")">
            #{goodsId}
        </foreach>
    </delete>
</mapper>

生成原理解析

导入表

实际为简单的插入操作

前端请求

Request URL: http://localhost/dev-api/tool/gen/importTable?tables=test2
Referrer Policy: no-referrer-when-downgrade

后端接收

GenController.java下的importTableSave

/**
 * 导入表结构(保存)
 */
@PreAuthorize("@ss.hasPermi('tool:gen:list')")//spring security中通过表达式控制方法权限
@Log(title = "代码生成", businessType = BusinessType.IMPORT)
@PostMapping("/importTable")
public AjaxResult importTableSave(String tables)
{
    String[] tableNames = Convert.toStrArray(tables);//用逗号进行了split
    // 查询表信息
    List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);//去数据库查这些表
    genTableService.importGenTable(tableList);//将信息插入到gen_table和gen_column两个表中
    return AjaxResult.success();
}

GenTableServiceImpl.java下的importGenTable

/**
 * 导入表结构
 * 
 * @param tableList 导入表列表
 */
@Override
@Transactional
public void importGenTable(List<GenTable> tableList)
{
    String operName = SecurityUtils.getUsername();
    try
    {
        for (GenTable table : tableList)
        {
            String tableName = table.getTableName();
            GenUtils.initTable(table, operName);//这里是在初始化table的信息,将表转换为类,类所需的大部分信息,如包,前缀等信息都在generator.yml中配置,operName只是用来简单set一下是谁创建的而已
            int row = genTableMapper.insertGenTable(table);//简单地调用mybatis的插入功能而已,动态sql需要判断是否为空,显得有些长
            if (row > 0)
            {
                // 保存列信息
                List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);//这里根据表的名字查询该表的字段名字,用到了information_schema.columns,SELECT  *  FROM information_schema.COLUMNS WHERE  TABLE_SCHEMA='数据库名'; 可以查询所有字段的name
                for (GenTableColumn column : genTableColumns)
                {
                    GenUtils.initColumnField(column, table);//将table的信息set到column中
                    genTableColumnMapper.insertGenTableColumn(column);//将column插入到GenTableColumn表中
                }
            }
        }
    }
    catch (Exception e)
    {
        throw new CustomException("导入失败:" + e.getMessage());
    }
}
生成代码

前端请求

在点击了生成按钮后,前端向后端发起了如下请求

Request URL: http://localhost/dev-api/tool/gen/batchGenCode?tables=sys_goods
Request Method: GET
Status Code: 204 Intercepted by the IDM Advanced Integration
Remote Address: 127.0.0.1:80
Referrer Policy: no-referrer-when-downgrade

后端接收

在GenController.java类下,有如下的方法用于接收请求

/**
 * 批量生成代码
 */
@PreAuthorize("@ss.hasPermi('tool:gen:code')")
@Log(title = "代码生成", businessType = BusinessType.GENCODE)
@GetMapping("/batchGenCode")
public void batchGenCode(HttpServletResponse response, String tables) throws IOException
{
    String[] tableNames = Convert.toStrArray(tables);
    byte[] data = genTableService.downloadCode(tableNames);
    genCode(response, data);//genCode:用来设置相应头,并将数据data传递给前端
}
  • genTableService.downloadCode(tableNames),由于自动注入的原因,它会调用genTableServiceImpl下的downloadCode
    @Override
    public byte[] downloadCode(String[] tableNames)
    {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableName : tableNames)//我只选了一张表来进行生成,所以tableNames的length为1
        {
            generatorCode(tableName, zip);
        }
        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }
  • generatorCode(tableName, zip),调用了当前类(genTableServiceImpl)下的方法
    private void generatorCode(String tableName, ZipOutputStream zip)
    {
        // 查询表信息
        GenTable table = genTableMapper.selectGenTableByName(tableName);//【1】
        // 设置主子表信息
        setSubTable(table);
        // 设置主键列信息
        setPkColumn(table);

        VelocityInitializer.initVelocity();

        VelocityContext context = VelocityUtils.prepareContext(table);//将table中的属性读出然后set到VelocityContext中,VelocityContex中有一个类型为Map的context属性

        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates)
        {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);//tpl是模板文件,就是以vm结尾的那些文件
            tpl.merge(context, sw);//这里是将context中的内容填充到tpl模板中留下的那些空中,最后将所有信息都填充到sw中,StringWriter是Writer和StringBuffer的结合体
            try
            {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
                IOUtils.write(sw.toString(), zip, Constants.UTF8);
                IOUtils.closeQuietly(sw);
				zip.flush();//执行后浏览器基本就可以进行下载了
                zip.closeEntry();
            }
            catch (IOException e)
            {
                log.error("渲染模板失败,表名:" + table.getTableName(), e);
            }
        }
    }
  • 【1】genTableMapper.selectGenTableByName(tableName);这里会调用dao接口,最后的sql xml文件如下,gen_table表的信息在导入的时候已经添加
	<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
	    SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
			   c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
		FROM gen_table t
			 LEFT JOIN gen_table_column c ON t.table_id = c.table_id
		where t.table_name = #{tableName} order by c.sort
	</select>
Logo

快速构建 Web 应用程序

更多推荐