1.动态SQL
动态SQL是mybatis的最强大特性之一,能够完成不同条件下,SQL语句的拼接.
1.1 <if>标签
在我们注册用户的时候,有些是必填信息,有些是选填信息.
这个时候,我们创建新用户的这个SQL语句就是不确定的.比如说有的用户填gender,有的用户不填gender,这时候SQL语句就会发生变化.
这时候我们就需要用动态标签来判断了.
当我们创建一个用户的时候,需要使用insert语句.

但是有些信息用户并不是必填项,当用户没有填的时候,就应该更改SQL语句.
如果我们用现在这个SQL,然后只传入两个参数,就会出现下面的情况,就会报错.

正常情况下,我们应该是如果需要username,password.
SQL语句应该是 insert into userinfo(username,'password') values(#{username},#{password})
如果还填了gender,SQL语句应该是:
insert into userinfo(username,'password',gender) values(#{username},#{password},#{gender})
我们需要的是这种动态的SQL.而不是和报错信息中的那样,不填的值为null.
要想实现这种效果,我们就需要用到<if>标签了.用if标签判断是否为空,如果为空,就不去添加这个字段进入SQL.

再次运行测试用例.

这时候我们就会发现,SQL语句倒是没有多余的信息了,但是后面多出来个逗号.还是会报错.

如果我们把,放在前面,当某个信息不填的时候一样也会报错.
这个时候,我们就需要用到<trim>标签了
1.2 <trim>标签
<trim>标签有四个属性.

用trim标签中的suffixOverrides去掉后缀的","再次运行程序.

这时候,我们再次运行代码,插入数据,就可以正确插入了.

1.3 <where>标签
在各种购物平台中,我们除了会进行排序操作以外,还会进行筛选操作.
这个时候,就需要用到where语句进行条件查询.用户对象中属性不为null的都可以作为查询条件.
原SQL:

使用where标签:

使用where标签之后,where标签会自动去除子句开头的and或者or.

试着查询年龄18的用户信息.

就可以查询出来结果.

1.4 <set>标签
需求:根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容.

根据id更改username


1.5 <foreach>标签
对集合进行遍历可以使用该标签.

删除年龄为18,20,21的用户信息.

这时候我们就需要使用foreach标签来进行循环遍历了.


运行代码看看效果.

1.6 <include>标签
在xml映射文件中配置的SQL,可能会存在很多重复的片段,此时就会有很多冗余的代码.
这时候,就可以用include标签来解决这个问题.

我们可以对重复的代码片段进行抽取,将其通过<sql>标签封装到一个SQL片段,然后再通过<include>标签进行引用.
<sql>:定义可重用的sql片段.
<include>:通过属性refid,指定包含的SQL片段.

通过这两个标签,可以重复使用同一个SQL片段.
运行代码,看看效果.

2.案例练习
2.1 表白墙
在之前的案例中,我们写了表白墙.
但是服务器一但重启,数据仍然会丢失.
想要数据不丢失,就得将数据存储在数据库中.
接下来,我们借助mybatis来实现这个功能.
2.1.1 创建表
SQL代码:
sql
DROP TABLE IF EXISTS message_info;
CREATE TABLE `message_info` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`from` VARCHAR ( 127 ) NOT NULL,
`to` VARCHAR ( 127 ) NOT NULL,
`message` VARCHAR ( 256 ) NOT NULL,
`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
2.1.2 引入MyBatis和MySQL驱动依赖
java
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
2.1.3 配置MySQL账号密码
java
spring:
application:
name: confessionWall
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
2.1.4 编写后端代码
创建实体类:
java
@Data
public class message {
private String id;
private String from;
private String to;
private String message;
private Integer deleteFlag;
private String creatTime;
private String updateTime;
}
创建MessageInfoMapper:
编写MyBatis代码:
java
public interface MessageInfoMapper {
//插入一条新信息
@Insert("insert into message_info(`from`, `to`, message) VALUES (#{from},#{to},#{message})")
Integer insert(Message message);
//获取所有消息
@Select("select `from`,`to`,message from message_info where delete_flag = 0")
List<Message> selectAll();
}
创建MessageService:
Service层用来处理业务逻辑:
这里的业务逻辑很简单,只需要添加数据到数据库,以及从数据库中获取数据即可.
java
public class MessageService {
@Autowired
private MessageInfoMapper messageInfoMapper;
public Integer insert(Message message){
return messageInfoMapper.insert(message);
}
public List<Message> selectAll(){
return messageInfoMapper.selectAll();
}
}
创建MessageController:
调用Service层,从数据库中获取消息.
从前端获取到message信息然后通过server层添加到数据库.
java
@RestController
@RequestMapping("/message")
public class MessageController {
@Autowired
private MessageService messageService;
//获取所有消息
@RequestMapping("/getMessage")
public List<Message> getMessage(){
return messageService.selectAll();
}
//点击提交按钮,添加一条数据
@RequestMapping("/push")
public Boolean push(Message message){
//判断数据是否为空
if(StringUtils.hasLength(message.getFrom())
&& StringUtils.hasLength(message.getTo())
&& StringUtils.hasLength(message.getMessage())
){
messageService.insert(message);
return true;
}
return false;
}
}
2.1.5 测试
运行程序,输入信息,点击提交,观察数据库是否成功添加.


点击提交,观察数据库.成功添加,

重新运行程序,记录依然存在

2.2 图书管理系统
前面的图书管理系统,我们只做了个用户登入界面和图书列表,并且数据是mock的.
接下来我们把其他的功能完善.
功能列表: 用户登入/图书列表/图书增删改查/翻页功能/强制登入
2.2.1 数据库表设计
java
-- 创建数据库
DROP DATABASE IF EXISTS book_test;
CREATE DATABASE book_test DEFAULT CHARACTER SET utf8mb4;
USE book_test;
-- 用户表
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (
`id` INT NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR ( 128 ) NOT NULL,
`password` VARCHAR ( 128 ) NOT NULL,
`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` ),
UNIQUE INDEX `user_name_UNIQUE` ( `user_name` ASC ))
ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '用户表';
-- 图书表
DROP TABLE IF EXISTS book_info;
CREATE TABLE `book_info` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`book_name` VARCHAR ( 127 ) NOT NULL,
`author` VARCHAR ( 127 ) NOT NULL,
`count` INT ( 11 ) NOT NULL,
`price` DECIMAL (7,2) NOT NULL,
`publish` VARCHAR ( 256 ) NOT NULL,
`status` TINYINT ( 4 ) DEFAULT 1 COMMENT '0-无效, 1-正常, 2-不允许借阅',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
-- 初始化数据
INSERT INTO user_info ( user_name, PASSWORD ) VALUES ( "admin", "admin" );
INSERT INTO user_info ( user_name, PASSWORD ) VALUES ( "zhangsan", "123456" );
-- 初始化图书数据
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('活着', '余华', 29, 22.00, '北京文艺出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('平凡的世界', '路遥', 5, 98.56, '北京十月文艺出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('三体', '刘慈欣', 9, 102.67, '重庆出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('金字塔原理', '麦肯锡', 16, 178.00, '民主与建设出版社');
创建完成后就有表book_info和表user_info


2.2.2 引入MyBatis和MySQL驱动依赖
添加这两个依赖

2.2.3 配置数据库&日志
java
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰自动转换
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句
# 设置日志文件的文件名
logging:
file:
name: spring-book.log
2.2.4 Model创建
UserInfo用户类:
java
public class UserInfo {
private Integer id;
private String userName;
private String password;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
BookInfo图书类:
java
public class BookInfo {
//图书ID
private Integer id;
//书名
private String bookName;
//作者
private String author;
//数量
private Integer count;
//定价
private BigDecimal price;
//出版社
private String publish;
//状态 0-无效 1-允许借阅 2-不允许借阅
private Integer status;
private String statusCN;
//创建时间
private Date createTime;
//更新时间
private Date updateTime;
}
2.2.5 用户登入
创建UserInfoMapper,用来从数据库中查询是否有该用户的信息,用于后面校验登入信息
java
@Mapper
public interface UserInfoMapper {
//从数据库中查询是否有这个用户,返回用户信息
@Select("select user_name,`password` from user_info where user_name = #{username} and delete_flag = 0")
UserInfo selectByUsername(String username);
}
创建UserService,处理用户登入逻辑.
java
@Service
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper;
public UserInfo selectByUsername(String username) {
return userInfoMapper.selectByUsername(username);
}
}
创建UserController,注入UserService,通过UserService的selectByUsername方法查询数据库中是否存在该用户信息,不存在返回false,存在则校验密码是否正确.
java
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("login")
public Boolean login(String username, String password, HttpSession session) {
if(!StringUtils.hasLength(username)|| !StringUtils.hasLength(password)){
return false;
}
//从数据库中查询是否存在用户
UserInfo userInfo = userService.selectByUsername(username);
if(userInfo == null){
return false;
}
if(userInfo!=null && password.equals(userInfo.getPassword())){
session.setAttribute("session_user_key", userInfo);
return true;
}
return false;
}
}
测试程序.
输入数据库中存在的账号密码,登入成功

2.2.6添加图书
约定前后端交互接口: book/addBook
先补充BookController代码:
java
@RequestMapping("/addBook")
public String addBook(BookInfo bookInfo){
//打印日志
log.info("添加图书:" + bookInfo);
//判断图书信息是否为空
if(!StringUtils.hasLength(bookInfo.getBookName())
|| !StringUtils.hasLength(bookInfo.getAuthor())
|| !StringUtils.hasLength(bookInfo.getPublish())
|| bookInfo.getPrice() == null
|| bookInfo.getCount() == null
|| bookInfo.getStatus() == null
){
return "信息不全,请补充图书信息";
}
Integer result = bookService.addBook(bookInfo);
if(result == 1){
return "";
}
return "添加图书失败";
}
再补充BookService代码:
java
public Integer addBook(BookInfo bookInfo) {
return bookInfoMapper.insertNewBook(bookInfo);
}
最后编写SQL语句:
java
@Insert("insert into book_info(book_name,author,count,price,publish,status) " +
"values(#{bookName},#{author},#{count},#{price},#{publish},#{status})")
Integer insertNewBook(BookInfo bookInfo);
写完后端代码之后,如果对前端相关内容不会修改,可以先用postman来进行测试.

发送请求后可以看到数据库中确实添加了一条数据.

前端代码:
修改add()方法:

程序测试:
点击确定,在数据库中成功添加一条数据


2.2.7图书列表
可以看到,我们添加图书之后,太哦转到图书列表页面,并没有显示刚刚添加的图书,这是因为之前我们在写这块代码的时候,图书信息是mock的,并不是从数据库中读取.
接下来我们来实现图书列表功能.
java
@RequestMapping("getList")
public List<BookInfo> getList(){
return bookService.getList();
}
java
public List<BookInfo> getList(){
List<BookInfo> list = bookInfoMapper.selectAll();
for(BookInfo bookInfo : list){
bookInfo.setStatusCN(BookStatus.getDescByCode(bookInfo.getStatus()));
}
return list;
}
java
@Select("select id,book_name,author,count,price,publish,status from book_info where status = 1")
List<BookInfo> selectAll();
添加了个枚举类,用来枚举图书状态,因为在数据库中status是状态码,而不是状态,所以我们要在service层中,先获取到数据库中的状态码,再设置状态.
java
public enum BookStatus {
DELETE(0, "无效"),
NORMAL(1, "可借"),
FORBIDDEN(2, "不可借");
private Integer code;
private String desc;
BookStatus(Integer code, String message) {
this.code = code;
this.desc = message;
}
public static String getDescByCode(Integer code) {
switch ( code){
case 0:
return DELETE.desc;
case 1:
return NORMAL.desc;
case 2:
return FORBIDDEN.desc;
default:
return null;
}
}
}
重新进入页面,我们就可以看到从数据库中拿到的真实信息了

2.2.8 翻页功能
先往数据库中添加多一点信息.当信息变多了之后我们会发现全在一页上.并不能像我们想象的那样分页.

需求:每一页展示十条数据.
想要实现这个功能,需要在数据库中进行分页查询.我们需要使用limit关键字.
查询第一页:
java
select * from book_info limit 0,10;
查询第二页:
java
select * from book_info limit 10,10;
观察上述SQL我们可以发现,在查询的时候,索引是一直在改变,但是每页显示的条数固定不变.
前端发起请求查询,需要两个参数,currentPage:当前页数,pageSize每页显示条数.
后端响应时,需要给前端的数据,records:所查询到的数据列表,total:总页数.
java
@Data
public class Pageinfo {
//当前页数
private Integer currentPage;
//每页显示条数
private Integer pageSize;
//索引
private Integer offset;
public Integer getOffset(){
return (currentPage - 1) * pageSize;
}
}
约定前后端交互接口:
前端给服务器发送一个/book/getListByPage这样的HTTP请求,通过currentPage告诉服务器,当前请求为第几页的数据,后端根据请求参数,返回对应页数的数据.
创建给前端返回的结果类.
java
@Data
public class PageResult<T> {
//总记录数
private Integer total;
//当前页中的数据
private List<T> records;
//当前页码
private PageInfo pageInfo;
public PageResult(List<T> records, Integer total, PageInfo pageInfo) {
this.pageInfo = pageInfo;
this.records = records;
this.total = total;
}
}
把原来的getList方法修改:
java
@RequestMapping("/getBookListByPage")
public PageResult<BookInfo> getBookListByPage(PageInfo pageinfo){
return bookService.getBookListByPage(pageinfo);
}
BookService修改:
获取到前端发来的请求页码,然后获取到索引,然后查询数据库,得到请求页码的数据,和总数.
构建新对象返回给前端.
java
public PageResult<BookInfo> getBookListByPage(PageInfo pageInfo) {
Integer setoff = pageInfo.getOffset();
Integer pagesize = pageInfo.getPageSize();
List<BookInfo> list = bookInfoMapper.seletByCurrentPage(setoff,pagesize);
for(BookInfo bookInfo : list){
bookInfo.setStatusCN(BookStatus.getDescByCode(bookInfo.getStatus()));
}
Integer count = bookInfoMapper.count();
return new PageResult<>(list,count,pageInfo);
}
BookInfoMapper:
java
@Select("select id,book_name,author,count,price,publish,status from book_info where status != 0 order by id asc limit #{currentPage},#{pageSize}")
List<BookInfo> seletByCurrentPage(Integer currentPage,Integer pageSize);
@Select("select count(*) from book_test.book_info where status != 0")
Integer count();
最后修改前端代码

运行程序,可以正常展示不同页码中的图书信息:

2.2.9 修改图书
约定前后端接口:
/book/queryBookById 进入修改页面显示当前图书信息
/book/updateBook 点击修改按钮,修改图书信息
controller层:
java
@RequestMapping("/queryBookById")
public BookInfo queryBookById(Integer bookId){
log.info("查询图书:"+ bookId);
return bookService.queryBookById(bookId);
}
@RequestMapping("/updateBook")
public String updateBook(BookInfo bookInfo){
if(bookInfo.getId()<=0){
return "参数不正确";
}
log.info("修改图书:"+ bookInfo);
try{
bookService.updateBook(bookInfo);
return "";
} catch (Exception e){
log.error("修改图书失败",e);
return e.getMessage();
}
}
service层:
java
public BookInfo queryBookById(Integer bookId) {
return bookInfoMapper.selectById(bookId);
}
public void updateBook(BookInfo bookInfo) {
bookInfoMapper.updateBook(bookInfo);
}
mapper层:
java
@Select("select id,book_name,author,count,price,publish,status from book_info where status != 0 and id = #{id}")
BookInfo selectById(Integer id);
//动态SQL用注解不好实现,我们采用xml方式实现
void updateBook(BookInfo bookInfo);
xml代码:
java
<update id="updateBook">
update book_info
<set>
<if test="bookName!=null">
book_name = #{bookName},
</if>
<if test="author!=null">
author = #{author},
</if>
<if test="price!=null">
price = #{price},
</if>
<if test="count!=null">
count = #{count},
</if>
<if test="publish!=null">
publish = #{publish},
</if>
<if test="status!=null">
status = #{status},
</if>
</set>
where id = #{id}
</update>
运行程序,结果符合预期,可以正常展现图书信息以及内容.

修改内容也可以正常修改.
2.2.10 删除图书
我们说的删除指的是逻辑删除.也就是将status设置为0
BookController:
java
@RequestMapping("/deleteBook")
public String deleteBook(Integer bookId){
log.info("删除图书:"+ bookId);
try{
bookService.deleteBook(bookId);
return "";
} catch (Exception e){
log.error("删除图书失败",e);
return e.getMessage();
}
}
BookService:
java
public void deleteBook(Integer bookId) {
bookInfoMapper.deleteBook(bookId);
}
mapper:
java
@Update("update book_info set status = 0 where id = #{bookId}")
void deleteBook(Integer bookId);
前端:

测试程序,点击删除:

图书从列表中消失,查看数据库,status设置为0

2.2.11 批量删除
批量删除其实就是批量修改数据.
BookController:
java
@RequestMapping("batchDeleteBook")
public String batchDeleteBook(@RequestParam List<Integer> ids){
log.info("批量删除图书:"+ ids);
try{
bookService.deleteBookByIds(ids);
return "";
} catch (Exception e){
log.error("批量删除图书失败",e);
return e.getMessage();
}
}
BookService:
java
public void deleteBookByIds(List<Integer> bookIds) {
bookInfoMapper.deleteBookByIds(bookIds);
}
SQL:
java
<delete id="deleteBookByIds">
update book_info
set status = 0
where id in
<foreach item="item" index="index" collection="bookIds" open="(" separator="," close=")">
#{item}
</foreach>
</delete>
前端:
测试程序:

点击确定,勾选书籍在列表中消失.
2.2.12 强制登入
虽然我们做了用户登入,但是我们发现,即使不登入,依然可以操作图书.
这是有极大风险的.所以我们需要强制登入.
我们之前已经将用户信息存储在了session中,那么我们就可以通过session中的信息来判断用户是否登入.
如果session中可以拿到用户信息,那么用户已经登入,可以进行后续操作,如果获取不到信息,就不能进行后续操作.要跳转到登入页面.
就以图书列表接口为例:查询到的结果不能判断用户是否登入

这个时候我们加一个属性告知后端的状态以及出错的原因.

当前只是图书列表一个功能,添加这个字段对我们的代码修改量是巨大的.我们可以对后端返回的数据进行一个包装.
设置一个返回结果,有返回数据,编码和错误信息

再降常数值放在同一个类里面,这样籽符合高内聚低耦合的思想.

修改后端代码.添加session判定.


最后修改前端代码.

重新运行程序.可以看到当我们没有登入的时候,就会强制跳转到登入页面
可以看到,我们想要实现强制登入这个功能,需要把所有接口都要改动,这个是很麻烦的,所以springboot也考虑到了这个,在后面的章节中,我们会学习有关拦截器相关知识,到时候会有更简单的方法来实现强制登入.
所以这里就只对图书列表功能进行修改.
3.MyBatis Generator
MyBatis Generator是一个为MyBatis框架设计的代码生成工具, 它可以根据数据库表结构自动生成相应的Java Model, Mapper接口以及SQL映射文件, 简化数据访问层的编码工作, 使得开发者可以更专注于业务逻辑的实现.
接下来我们看下, 如何使用MyBatis Generator 来生成代码.
3.1引入插件
java
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<executions>
<execution>
<id>Generate MyBatis Artifacts</id>
<phase>deploy</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<!--generator配置文件所在位置-->
<configurationFile>src/main/resources/mybatisGenerator/generatorConfig.xml</configurationFile>
<!-- 允许覆盖生成的文件,mapxml不会覆盖,采用追加的方式-->
<overwrite>true</overwrite>
<verbose>true</verbose>
<!--将当前pom的依赖项添加到生成器的类路径中-->
<includeCompileDependencies>true</includeCompileDependencies>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
</plugin>
3.2 添加generatorGonfig.xml并修改
创建对应路径的文件.


完善文件内容:
java
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!-- 配置生成器 -->
<generatorConfiguration>
<!-- 一个数据库一个context -->
<context id="MysqlTables" targetRuntime="MyBatis3Simple">
<!--禁用自动生成的注释-->
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接信息-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/java_blog_spring?serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true"
userId="root"
password="root">
</jdbcConnection>
<!-- 生成实体类, 配置路径 -->
<javaModelGenerator targetPackage="com.example.demo.model"
targetProject="src/main/java" >
<property name="enableSubPackages" value="false"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成mapxml文件 -->
<sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources" >
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 生成mapxml对应client, 也就是接口dao -->
<javaClientGenerator targetPackage="com.example.demo.mapper"
targetProject="src/main/java" type="XMLMAPPER" >
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- table可以有多个,tableName表示要匹配的数据库表 -->
<table tableName="user" domainObjectName="UserInfo"
enableSelectByExample="true" enableDeleteByExample="true" enableDeleteByPrimaryKey="true"
enableCountByExample="true"
enableUpdateByExample="true">
<!-- 类的属性是否用数据库中的真实字段名做为属性名, 不指定这个属性会自动转换 _ 为驼峰命名规则 -->
<property name="useActualColumnNames" value="false" />
<!-- 数据库表主键 -->
<generatedKey column="id" sqlStatement="Mysql" identity="true" />
</table>
</context>
</generatorConfiguration>
再右边的Maven中找到这个插件,然后双击运行即可.
