购买方式有2种:① 微信加博主微信支付,手动微信发货 ② 支付宝直接支付,自动发货到邮箱 (均提供售后)
上周在家抽空做了一个代码生成器,因为博主给大家做项目的时候大部分技术架构都是 SpringBoot+MyBatisPlus+Thymleaf 架构组成。
每次开启一个新项目时往往需要做很多增删改查操作,很浪费时间
比如新建一个 t_test 表后,需要创建 Test.java,TestMapper.java,TestService.java,TestServiceImpl.java,TestController.java 等java文件,
还有 admin_test.html、admin_test_edit.html 等html前端文件。
能否实现一个功能,一键生成这7个文件呢?当然可以,之前我们用过很多代码生成器工具,如 MyBatisGenerator和MyBatisPlusGenerator,
借助其思想,我们也动手搞一个。
涉及到的技术主要是 FreeMaker
注意,本文只贴核心代码,如果需要完整代码,请联系博主
一、引入依赖
<!-- freemark -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
二、freemaerker 简单测试一下
1、先准备一个 test.ftl,里面内容如下
public class ${className} {
}
2、创建测试类
package com.liuyanzhao.springboot.code.generator;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
public class FreemarkerTest {
public static void main(String[] args) throws IOException, TemplateException {
Configuration configuration = new Configuration(Configuration.getVersion());
// configuration.setDirectoryForTemplateLoading(new File("F:\workspace\code2022\SpringBootCodeGenerator\src\main\resources\templates\code-generator\mybatis-plus"));
// 在引入jar包的情况,必须通过如下方法获取jar包里的模板文件
configuration.setClassForTemplateLoading(FreemarkerTest.class, "/templates/code-generator/mybatis-plus");
configuration.setTemplateLoader(new ClassTemplateLoader(FreemarkerTest.class, "/templates/code-generator/mybatis-plus"));
configuration.setDefaultEncoding("utf-8");
Template template = configuration.getTemplate("test.ftl");
Map<String, Object> dataModel = new HashMap(2);
dataModel.put("className", "Demo");
File file = new File("F:\\workspace\\code2022\\SpringBootCodeGenerator\\output\\Demo.java");
if (file.exists()) {
file.delete();
}
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
Writer out = new FileWriter(file);
template.process(dataModel, out);
}
}
3、代码结构和运行结果示意图
三、MyBatisPlus框架的增删改查代码
1、实体类
package ${commonInfo.packageName}.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* ${classInfo.classComment} 实体类
*
* @author ${commonInfo.authorName}
* @date ${commonInfo.createDate}
*/
@Data
@TableName("${classInfo.tableName}")
public class ${classInfo.className} implements Serializable {
<#if classInfo.fieldList?exists && classInfo.fieldList?size gt 0>
<#list classInfo.fieldList as fieldItem >
/**
* ${fieldItem.fieldComment}
*/
<#if fieldItem.isPrimaryKey?exists && fieldItem.isPrimaryKey == true>
@TableId(type = IdType.AUTO)
</#if>
private ${fieldItem.fieldClass} ${fieldItem.fieldName};
</#list>
</#if>
}
2、Mapper接口
package ${commonInfo.packageName}.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import ${commonInfo.packageName}.entity.${classInfo.className};
import org.apache.ibatis.annotations.Mapper;
/**
* ${classInfo.classComment} Mapper接口,提供数据访问
*
* @author ${commonInfo.authorName}
* @date ${commonInfo.createDate}
*/
@Mapper
public interface ${classInfo.className}Mapper extends BaseMapper<${classInfo.className}> {
}
3、Service接口
package ${commonInfo.packageName}.service;
import com.baomidou.mybatisplus.extension.service.IService;
import ${commonInfo.packageName}.entity.${classInfo.className};
import java.util.List;
/**
* ${classInfo.classComment} Service接口,为Controller服务
*
* @author ${commonInfo.authorName}
* @date ${commonInfo.createDate}
*/
public interface ${classInfo.className}Service extends IService<${classInfo.className}> {
}
4、Service实现类
package ${commonInfo.packageName}.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import ${commonInfo.packageName}.entity.${classInfo.className};
import ${commonInfo.packageName}.mapper.${classInfo.className}Mapper;
import ${commonInfo.packageName}.service.${classInfo.className}Service;
import org.springframework.stereotype.Service;
/**
* ${classInfo.classComment} Service接口的实现类,该实现类会调用Mapper层
*
* @author ${commonInfo.authorName}
* @date ${commonInfo.createDate}
*/
@Service
public class ${classInfo.className}ServiceImpl extends ServiceImpl<${classInfo.className}Mapper, ${classInfo.className}> implements ${classInfo.className}Service {
}
5、Controller类
package ${commonInfo.packageName}.controller.admin;;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ${commonInfo.packageName}.common.config.api.ApiResource;
import ${commonInfo.packageName}.common.util.CommonUtil;
import ${commonInfo.packageName}.common.util.PageUtil;
import ${commonInfo.packageName}.common.util.StringUtils;
import ${commonInfo.packageName}.common.vo.JsonResult;
import ${commonInfo.packageName}.common.vo.PageVo;
import ${commonInfo.packageName}.controller.common.BaseController;
import ${commonInfo.packageName}.entity.${classInfo.className};
import ${commonInfo.packageName}.service.${classInfo.className}Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* 后台${classInfo.classComment}管理控制器
*/
@Slf4j
@Controller
@RequestMapping(value = "/admin/${classInfo.classNameLowerCase}")
@ApiResource(apiName = "${classInfo.classComment}管理", resMenuFlag = true, resIcon = "fa fa-list", resOrder = 10)
public class ${classInfo.className}Controller extends BaseController {
@Autowired
private ${classInfo.className}Service ${classInfo.classNameLowerCase}Service;
/************以下是页面 控制器方法,返回值即html文件路径(templates下面),浏览器直接访问 ************/
/**
* ${classInfo.classComment}列表页面
*
* @return 模板路径
*/
@GetMapping
@ApiResource(apiName = "${classInfo.classComment}列表", resMenuFlag = true, resOrder = 1)
public String listPage(
@RequestParam(value = "keywords", defaultValue = "") String keywords,
@ModelAttribute PageVo pageVo,
HttpServletRequest request,
Model model) {
// 分页入参处理
Page page = PageUtil.initMpPage(pageVo);
// 查询条件构造
LambdaQueryWrapper<${classInfo.className}> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotEmpty(keywords)) {
${classInfo.queryWrapperLike}
}
<#if classInfo.hasUserId>
if(!loginUserIsAdmin(request)) {
queryWrapper.eq(${classInfo.className}::getUserId, getLoginUserId(request));
}
</#if>
// 调用分页查询
IPage<${classInfo.className}> pageInfo = ${classInfo.classNameLowerCase}Service.page(page, queryWrapper);
// 把结果存储在model中,前端可以通过占位符EL表达式获取数据
model.addAttribute("pageInfo", pageInfo);
model.addAttribute("keywords", keywords);
return "admin/${classInfo.classNameLowerCase}/admin_${classInfo.classNameLowerCase}";
}
/**
* 创建${classInfo.classComment}页面
*
* @return 模板路径
*/
@GetMapping("/new")
@ApiResource(apiName = "添加${classInfo.classComment}", resMenuFlag = true, resOrder = 2)
public String newPage(Model model) {
model.addAttribute("item", new ${classInfo.className}());
return "admin/${classInfo.classNameLowerCase}/admin_${classInfo.classNameLowerCase}_edit";
}
/**
* 编辑${classInfo.classComment}页面
*
* @return 模板路径
*/
@GetMapping("/edit")
@ApiResource(apiName = "编辑${classInfo.classComment}页面")
public String editPage(@RequestParam("id") Long id, Model model, HttpServletRequest request) {
${classInfo.className} ${classInfo.classNameLowerCase} = ${classInfo.classNameLowerCase}Service.getById(id);
if (${classInfo.classNameLowerCase} == null) {
return this.renderNotFound();
}
<#if classInfo.hasUserId>
if (!loginUserIsAdmin(request) && !Objects.equals(${classInfo.classNameLowerCase}.getUserId(), getLoginUserId(request))) {
return this.renderNotAllowAccess();
}
</#if>
model.addAttribute("item", ${classInfo.classNameLowerCase});
return "admin/${classInfo.classNameLowerCase}/admin_${classInfo.classNameLowerCase}_edit";
}
/************以上是页面 控制器方法,返回值即html文件路径(templates下面),浏览器直接访问 ************/
/************以下是接口操作 控制器方法,返回JSON数据,为前端ajax服务 ************/
/**
* 删除${classInfo.classComment}提交操作
*
* @param id ${classInfo.classComment}Id
* @return JsonResult
*/
@ApiResource(apiName = "删除${classInfo.classComment}操作")
@PostMapping(value = "/delete")
@ResponseBody
public JsonResult deleteSubmit(@RequestParam("id") Long id, HttpServletRequest request) {
<#if classInfo.hasUserId>
this.checkDataPermission(id, request);
</#if>
${classInfo.classNameLowerCase}Service.removeById(id);
return JsonResult.success("删除成功");
}
/**
* 批量删除${classInfo.classComment}提交操作
*
* @param ids ${classInfo.classComment}ID列表
* @return 响应JSON
*/
@PostMapping(value = "/batchDelete")
@ResponseBody
@ApiResource(apiName = "批量删除${classInfo.classComment}操作")
public JsonResult batchDeleteSubmit(@RequestParam("ids") List<Long> ids, HttpServletRequest request) {
if (CommonUtil.isNotEmpty(ids)) {
<#if classInfo.hasUserId>
for (Long id : ids) {
this.checkDataPermission(id, request);
}
</#if>
${classInfo.classNameLowerCase}Service.removeByIds(ids);
}
return JsonResult.success("删除成功");
}
/**
* 保存${classInfo.classComment}操作
*
* @param ${classInfo.classNameLowerCase} ${classInfo.classNameLowerCase}
* @return 响应JSON
*/
@PostMapping(value = "/save")
@ResponseBody
@ApiResource(apiName = "保存${classInfo.classComment}操作")
public JsonResult saveSubmit(@ModelAttribute ${classInfo.className} ${classInfo.classNameLowerCase}, HttpServletRequest request) {
<#if classInfo.hasUserId>
if (${classInfo.classNameLowerCase}.getId() != null) {
this.checkDataPermission(${classInfo.classNameLowerCase}.getId(), request);
}
${classInfo.classNameLowerCase}.setUserId(getLoginUserId(request));
</#if>
${classInfo.classNameLowerCase}.setCreateTime(new Date());
${classInfo.classNameLowerCase}Service.saveOrUpdate(${classInfo.classNameLowerCase});
return JsonResult.success("保存成功");
}
<#if classInfo.hasUserId>
/**
* 检查有没有数据权限
*
* @param id
* @param request
*/
private void checkDataPermission(Long id, HttpServletRequest request) {
Test check = testService.getById(id);
if (check == null) {
throw new RuntimeException("数据不存在");
}
if (!loginUserIsAdmin(request) && !Objects.equals(check.getUserId(), getLoginUserId(request))) {
throw new RuntimeException("没有权限操作");
}
}
</#if>
/************以上是接口操作 控制器方法,返回JSON数据,为前端ajax服务 ************/
}
四、Thymeleaf 的前端页面代码
1、列表页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:replace="admin/module/_header"></div>
<div class="wrapper">
<div th:replace="admin/module/_sidebar"></div>
<div class="content-wrapper">
<style>
</style>
<section class="content-header">
<h1 style="display: inline-block;">${classInfo.classComment}管理</h1>
<ol class="breadcrumb">
<li><a data-pjax="true" href="/admin"><i class="fa fa-dashboard"></i> 首页</a></li>
<li><a data-pjax="true" href="/admin/${classInfo.classNameLowerCase}">${classInfo.classComment}管理</a></li>
<li class="active">${classInfo.classComment}列表</li>
</ol>
</section>
<section class="content container-fluid">
<div class="row">
<div class="col-xs-12">
<div class="col-md-4 col-xs-12" style="margin-bottom: 10px;padding-left: 0;">
<a class="btn btn-primary" href="/admin/${classInfo.classNameLowerCase}/new"
th:if="井{session.user.authUrls.contains('/admin/${classInfo.classNameLowerCase}/new')}">
<i class="fa fa-plus"></i> 新增
</a>
<button type="button" class="btn btn-danger" onclick="doBatchDelete()"
th:if="井{session.user.authUrls.contains('/admin/${classInfo.classNameLowerCase}/batchDelete')}">
<i class="fa fa-remove"></i> 批量删除
</button>
</div>
<div class="col-md-8 col-xs-12"
style="margin-bottom: 10px; text-align:right;padding-left: 0; padding-right: 0;">
<form class="form-inline" action="/admin/${classInfo.classNameLowerCase}">
<div class="form-group">
<input type="text" class="form-control" id="keywords" name="keywords"
th:value="井{keywords}" placeholder="请输入关键字">
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
</form>
</div>
</div>
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-body table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th><input type="checkbox" id="allSelect" onclick="doCheck()"></th>
<#if classInfo.fieldList?exists && classInfo.fieldList?size gt 0>
<#list classInfo.fieldList as fieldItem >
<#if fieldItem.fieldName != 'userId' && fieldItem.fieldName != 'id' && fieldItem.fieldName != 'content'>
<th>${fieldItem.fieldSimpleComment}</th>
</#if>
</#list>
</#if>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="item : 井{pageInfo.records}">
<td><input type="checkbox" name="ids" th:value="井{item.id}"></td>
<#if classInfo.fieldList?exists && classInfo.fieldList?size gt 0>
<#list classInfo.fieldList as fieldItem >
<#if fieldItem.fieldName != 'userId' && fieldItem.fieldName != 'id' && fieldItem.fieldName != 'content'>
<#if fieldItem.isInput && fieldItem.fieldClass != "Date">
<td>[[井{item.${fieldItem.fieldName}}]]</td>
</#if>
<#if fieldItem.isInput && fieldItem.fieldClass == "Date">
<td>[[井{#dates.format(item.${fieldItem.fieldName},'yyyy-MM-dd
HH:mm:ss')}]]
</td>
</#if>
<#if fieldItem.isRadio>
<td>
<#list fieldItem.valueTextList as valueText >
<span th:if="井{item.${fieldItem.fieldName} == '${valueText.value}'}">${valueText.text}</span>
</#list>
</td>
</#if>
<#if fieldItem.isCheckbox>
<td>
<#list fieldItem.valueTextList as valueText >
<span th:if="井{item.${fieldItem.fieldName}.contains('${valueText.value}')}">${valueText.text}</span>
</#list>
</td>
</#if>
<#if fieldItem.isSelect>
<td>
<#list fieldItem.valueTextList as valueText >
<span th:if="井{item.${fieldItem.fieldName} == '${valueText.value}'}">${valueText.text}</span>
</#list>
</td>
</#if>
</#if>
</#list>
</#if>
<td>
<a th:if="井{session.user.authUrls.contains('/admin/${classInfo.classNameLowerCase}/edit')}"
th:href="'/admin/${classInfo.classNameLowerCase}/edit?id='+井{item.id}"
class="btn btn-info btn-xs ">编辑</a>
<a th:if="井{session.user.authUrls.contains('/admin/${classInfo.classNameLowerCase}/delete')}"
href="javascript:void(0)" th:onclick="'doDelete('+井{item.id}+')'"
class="btn btn-danger btn-xs ">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<div class="box-footer clearfix">
<div class="no-margin pull-left">
第 [[井{pageInfo.current}]] / [[井{pageInfo.pages}]] 页
</div>
<ul class="pagination no-margin pull-right">
<li><a data-pjax="true" th:classappend="井{pageInfo.current == 1 ? 'disabled' : ''}"
class="btn btn-sm"
th:href="'/admin/${classInfo.classNameLowerCase}?keywords='+井{keywords}">
首页
</a>
</li>
<li><a data-pjax="true" class="btn btn-sm"
th:classappend="井{pageInfo.current == 1 ? 'disabled' : ''}"
th:href="'/admin/${classInfo.classNameLowerCase}?page='+井{pageInfo.current - 1}+'&keywords='+井{keywords}">
上一页
</a>
</li>
<li><a data-pjax="true" class="btn btn-sm"
th:classappend="井{pageInfo.current == pageInfo.pages ? 'disabled' : ''}"
th:href="'/admin/${classInfo.classNameLowerCase}?page='+井{pageInfo.current + 1}+'&keywords='+井{keywords}">
下一页
</a>
</li>
<li><a data-pjax="true" class="btn btn-sm"
th:classappend="井{pageInfo.current == pageInfo.pages ? 'disabled' : ''}"
th:href="'/admin/${classInfo.classNameLowerCase}?page='+井{pageInfo.pages}+'&keywords='+井{keywords}">
尾页
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<script>
function doDelete(id) {
layer.confirm("你确定是否删除?", function (index) {
$.ajax({
type: "POST",
url: '/admin/${classInfo.classNameLowerCase}/delete',
async: false,
data: {
'id': id
},
success: function (data) {
if (data.code == 0) {
showMsg(data.msg, "error", 1000);
} else {
showMsgAndReload(data.msg, "success", 1000);
}
}
});
layer.close(index);
});
}
function doBatchDelete() {
var arr = [];
$('input[name="ids"]:checked').each(function () {
arr.push($(this).val());
});
if (arr.length <= 0) {
showMsg('请选择要删除的记录', "error", 1000);
} else {
layer.confirm('你确定是否删除?', function (index) {
$.ajax({
url: "/admin/${classInfo.classNameLowerCase}/batchDelete?ids=" + arr,
type: "POST",
success: function (data) {
if (data.code == 0) {
showMsg(data.msg, "error", 1000);
} else {
showMsgAndReload(data.msg, "success", 1000);
}
}
});
layer.close(index);
});
}
}
</script>
</div>
</div>
<div th:replace="admin/module/_footer"></div>
2、创建/修改页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:replace="admin/module/_header"></div>
<div class="wrapper">
<!-- 菜单栏模块 -->
<div th:replace="admin/module/_sidebar"></div>
<div class="content-wrapper">
<style>
</style>
<section class="content-header">
<h1>
[[井{item.id == null ? '添加' : '修改'}]]${classInfo.classComment}
</h1>
<ol class="breadcrumb">
<li>
<a data-pjax="true" href="/admin"><i class="fa fa-dashboard"></i> 首页</a>
</li>
<li><a data-pjax="true" href="/admin/${classInfo.classNameLowerCase}">${classInfo.classComment}管理</a>
</li>
<li class="active">[[井{item.id == null ? '添加' : '修改'}]]${classInfo.classComment}</li>
</ol>
</section>
<!-- tab选项卡 -->
<section class="content container-fluid">
<div class="row">
<form method="post" id="form">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">[[井{item.id == null ? '添加' : '修改'}]]用户</h3>
</div>
<div class="box-body">
<input type="hidden" name="id" th:value="井{item.id}">
<#if classInfo.fieldList?exists && classInfo.fieldList?size gt 0>
<#list classInfo.fieldList as fieldItem >
<#if fieldItem.isInput
&& fieldItem.fieldName != "id"
&& fieldItem.fieldName != 'createTime' >
<div class="form-group">
<label>${fieldItem.fieldSimpleComment}</label>
<input type="text" class="form-control <#if fieldItem.isNullable>notEmpty</#if>"
title="${fieldItem.fieldSimpleComment}" id="${fieldItem.fieldName}"
name="${fieldItem.fieldName}"
th:value="井{item.${fieldItem.fieldName}}">
</div>
</#if>
<#if fieldItem.valueTextList?exists && fieldItem.valueTextList?size gt 0 && fieldItem.isRadio>
<div class="form-group">
<label>${fieldItem.fieldSimpleComment}</label> <br>
<#list fieldItem.valueTextList as valueText >
<label class="radio-inline">
<input type="radio" name="${fieldItem.fieldName}"
value="${valueText.value}"
th:checked="井{item.${fieldItem.fieldName}== '${valueText.value}'}">${valueText.text}
</label>
</#list>
</div>
</#if>
<#if fieldItem.valueTextList?exists && fieldItem.valueTextList?size gt 0 && fieldItem.isCheckbox>
<div class="form-group">
<label>${fieldItem.fieldSimpleComment}</label> <br>
<#list fieldItem.valueTextList as valueText >
<label class="checkbox-inline">
<input type="checkbox" name="${fieldItem.fieldName}"
value="${valueText.value}"
th:checked="井{item.${fieldItem.fieldName} != null && item.${fieldItem.fieldName}.contains('${valueText.value}')}">${valueText.text}
</label>
</#list>
</div>
</#if>
<#if fieldItem.valueTextList?exists && fieldItem.valueTextList?size gt 0 && fieldItem.isSelect>
<div class="form-group">
<label>${fieldItem.fieldSimpleComment}</label>
<select class="form-control" name="${fieldItem.fieldName}"
th:value="井{item.${fieldItem.fieldName}}">
<#list fieldItem.valueTextList as valueText >
<option value="${valueText.value}"
th:selected="井{item.${fieldItem.fieldName} == '${valueText.value}'}">${valueText.text}</option>
</#list>
</select>
</div>
</#if>
</#list>
</#if>
</div>
<div class="box-footer">
<button type="button" class="btn btn-primary btn-sm "
th:if="井{session.user.authUrls.contains('/admin/${classInfo.classNameLowerCase}/save')}"
onclick="save()">保存
</button>
<a href="/admin/${classInfo.classNameLowerCase}"
class="btn btn-info btn-sm">返回</a>
</div>
</div>
</div>
</form>
</div>
</section>
<script th:inline="javascript">
<#if classInfo.hasContent>
tinymce.init({
selector: '#content',
language: "zh_CN",
height: 600,
theme: "silver",
browser_spellcheck: true, // 拼写检查
branding: true, // 去水印
statusbar: false, // 隐藏编辑器底部的状态栏
paste_data_images: true, // 允许粘贴图像
menubar: true, // 隐藏最上方menu
fontsize_formats:
"12px 13px 14px 15px 16px 17px 18px 20px 22px 24px 26px 30px 35px 40px 50px",
plugins:
"print preview fullpage searchreplace autolink directionality visualblocks visualchars fullscreen image link media template codesample table charmap hr pagebreak nonbreaking anchor toc insertdatetime advlist lists textcolor wordcount imagetools contextmenu colorpicker textpattern help code",
toolbar:
"formatselect | bold italic strikethrough forecolor backcolor fontsizeselect | link image | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat| code",
paste_webkit_styles: true,
paste_data_images: true, // 设置为允许粘帖图片
images_upload_url: '/admin/file/upload', // 图片上传地址
contextmenu: `inserttable | cell row column deletetable`,
relative_urls: false,
remove_script_host: false
});
</#if>
function save() {
// 判断空
if (!checkNotEmpty()) {
return
}
var param = $('#form').serialize();
<#if classInfo.hasContent>
var content = tinymce.activeEditor.getContent();
param = $.param({'content': content}) + '&' + $('#form').serialize();
</#if>
// 请求后台
$.ajax({
type: 'post',
url: '/admin/${classInfo.classNameLowerCase}/save',
data: param,
success: function (data) {
console.log(data);
if (data.code == 1) {
showMsgAndRedirect(data.msg, "success", 1000, "/admin/${classInfo.classNameLowerCase}");
} else {
showMsg(data.msg, "error", 1000);
}
}
});
}
</script>
</div>
</div>
<div th:replace="admin/module/_footer"></div>
五、查询数据库数据封装参数
相关代码有点多,主要是利用 JDBC 查询数据库表字段信息
根据根据表结构的来解析字段类型,以及页面是用输入框、单选框、多选框、下拉框等
表结构示意图如下
代码的话有点多,这里贴下代码结构截图,需要完整代码联系博主
六、开始使用
1、引入依赖
<!-- 代码生成器-->
<dependency>
<groupId>com.liuyanzhao</groupId>
<artifactId>BootTemplateGenerator</artifactId>
<version>1.0.0</version>
</dependency>
目前该依赖没有上传到中央仓库,需要jar包或者源码请联系博主
2、创建代码生成类
package com.liuyanzhao;
import com.liuyanzhao.springboot.code.generator.GenerateCode;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.util.List;
/**
* 代码生成
*/
public class CodeGenerateApplication {
// 数据库名
public static final String DATABASE_NAME = "boot_template";
// 数据库用户名
public static final String USERNAME = "root";
// 数据库密码
public static final String PASSWORD = "123456";
// 需要生成代码的表
public static final String[] TABLE_NAME = {"t_test"};
// 表名前缀(可选)
public static final String TABLE_PREFIX = "t_";
// 包名
public static final String PACKAGE_NAME = "com.liuyanzhao";
// 代码生成目标路径
public static final String OUTPUT_PATH = "F:/workspace/code2022/BootTemplate";
public static void main(String[] args) throws TemplateException, IOException {
List<String> fileList = GenerateCode.generate(DATABASE_NAME, USERNAME, PASSWORD, PACKAGE_NAME, TABLE_NAME, TABLE_PREFIX, OUTPUT_PATH);
System.out.println("代码生成成功,如下:");
for (String file : fileList) {
System.out.println(file);
}
}
}
七、代码效果图
1、表结构
2、运行上面测试类
3、Java代码图
1)entity、mapper、service、serviceImpl 的截图
2) controller 类截图
3、HTML页面图
1)list页面
2)编辑页面
八、页面效果图
我们允许一下我这个模板项目代码,看下
1、列表页面
2、添加编辑页面
然后测试添加数据,编辑数据,删除数据都没有问题