深入学习Mybatis-plus
?了解Mybatis-plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
-
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,
-
更有强大的条件构造器,满足各类使用需求
-
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
-
支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、
-
SQLServer2005、SQLServer 等多种数据库
-
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解
-
决主键问题
-
支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
-
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操
-
作
-
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
-
支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词
-
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,
-
支持模板引擎,更有超多自定义配置等您来使用
-
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List
-
查询
-
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
-
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
-
内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击
1.2架构
?? Mybatis-Plus 快速开始
对于Mybatis整合MP有常常有三种用法,分别是Mybatis+MP、Spring+Mybatis+MP、Spring Boot+Mybatis+MP。
2.1创建数据库以及表
-- 创建数据库
CREATE DATABASE mp CHARSET 'utf8';
?
-- 创建表
CREATE TABLE tb_user(
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
username VARCHAR(20) NOT NULL COMMENT'用户名',
PASSWORD VARCHAR(20) NOT NULL COMMENT'密码',
NAME VARCHAR(30) DEFAULT NULL COMMENT'姓名',
age INT DEFAULT NULL COMMENT'年龄',
email VARCHAR(50) DEFAULT NULL COMMENT'邮箱'
)CHARSET = utf8;
2.2、创建工程
<groupId>priv.chenJiegroupId>
<artifactId>myBatis-plus-1artifactId>
<packaging>pompackaging>
2.3、Mybatis 实现查询User
第一步 编写mybatis-confifig.xml文件:
<?xml version="1.0" encoding="UTF-8" ?> DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
?
<typeAliases>
<typeAlias type="priv.cj.pojo.User" alias="user"/>
typeAliases>
?
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mp? useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQuerie s=true&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
?
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
configuration>
第二步 编写User实体对象:(这里使用lombok进行了进化bean操作)
第三步 编写UserMapper接口:
public interface UserMapper {
List<User> findAll();
}
第四步 编写UserMapper.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">
?
<mapper namespace="priv.cj.mapper.UserMapper">
<select id="findAll" resultType="user">
select * from tb_user
select>
mapper>
第五步 编写TestMybatis测试用例:
2.4、Mybatis +MP实现查询User
第一步 将UserMapper继承BaseMapper,将拥有了BaseMapper中的所有方法:
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
第二步 使用MP中的MybatisSqlSessionFactoryBuilder进程构建:
@Test[tips] 在User对象中添加@TableName,指定数据库表名
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//由于使用了MybatisSqlSessionFactoryBuilder进行了构建,继承的BaseMapper中的方法就载入到了SqlSession中,所以就可以直接使用相关的方法;
SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 可以调用BaseMapper中定义的方法
Listall = mapper.selectList(null);
for (User user : all) {
System.out.println(user);
}
}
2.5、Spring + Mybatis + MP
引入了Spring框架,数据源、构建等工作就交给了Spring管理。
第一步 创建model
<?xml version="1.0" encoding="UTF-8"?>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
priv.chenJie
myBatis-plus-1
1.0-SNAPSHOT
4.0.0
myBatis-plus-spring
5.2.9.RELEASE
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-test
${spring.version}
第二步 编写jdbc.properties及log4j.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mp
jdbc.username=root
jdbc.password=root
log4j.rootLogger=DEBUG,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
第三步 编写applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cintext="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
第四步 编写User对象以及UserMapper接口:
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
}
public interface UserMapper extends BaseMapper{
}
第五步 编写测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestMybatisSpring {
@Autowired
private UserMapper userMapper;
@Test
public void test1(){
Listusers = userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
}
2.6、SpringBoot + Mybatis + MP
使用SpringBoot将进一步的简化MP的整合,需要注意的是,由于使用SpringBoot需要继承parent,所以需要重新创建工程,并不是创建子Module。
第一步 创建工程继承parent并导入依赖
<?xml version="1.0" encoding="UTF-8"?>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.2
priv.chenJie
myBatis-plus-springboot
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-logging
org.springframework.boot
spring-boot-starter-test
2.6.2
org.projectlombok
lombok
1.18.12
com.baomidou
mybatis-plus-boot-starter
3.1.1
mysql
mysql-connector-java
5.1.32
org.slf4j
slf4j-log4j12
1.7.32
第二步 编写application.properties及log4j.properties
spring.application.name=myBatis-plus-springboot //当前项目名
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mp
spring.datasource.username=root
spring.datasource.password=root
第四步 编写pojo及mapper
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
}
@Mapper
public interface UserMapper extends BaseMapper{
}
第五步 编写启动类
@MapperScan("priv.cj.mapper") //设置mapper接口的扫描包
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
第六步 编写测试类
@SpringBootTest
public class TestMybatisSpringBoot {
@Autowired
private UserMapper userMapper;
@Test
public void test1(){
Listusers = this.userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
}
?? Mybatis-Plus实现CURD
通过前面的学习,我们了解到通过继承BaseMapper就可以获取到各种各样的单表操作,接下来我们将详细讲解这些 操作。
3.1插入操作
@Test
public void testInsert() {
User user = new User();
user.setAge(20);
user.setMail("test@itcast.cn");
user.setName("曹操");
user.setUserName("caocao");
user.setPassword("123456");
user.setFavorite("陈洁");
int result = this.userMapper.insert(user); //返回的result是受影响的行数,并不是自增后的id
System.out.println("result = " + result);
System.out.println(user.getId()); //自增后的id会回填到对象中
}
还需要设置id的生成策略,期望的是数据库自增长
//设置id自增长
@TableId(type = IdType.AUTO)
private Long id;
@TableField
在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个:
1、对象中的属性名和字段名不一致的问题(非驼峰) ? 2、对象中的属性字段在表中不存在的问题
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
@TableId(type = IdType.AUTO)//设置id自增长
private Long id;
private String userName;
@TableField(select = false)//查询时不返回当前字段中的值
private String password;
private String name;
private Integer age;
@TableField(value = "email")//解决字段名不一致
private String mail;
@TableField(exist = false)//解决对象字段在表中不存在的问题
private String favorite;
}
3.2 更新操作
在MP中,更新操作有2种,一种是根据id更新,另一种是根据条件更新。
3.2.1 根据id更新
@Test
public void testUpdateById() {
User user = new User();
user.setId(4L);
user.setUserName("chenJie");
user.setPassword("loveJT1314");
user.setMail("loveCJ.com");
int result = userMapper.updateById(user);
System.out.println("result =>" + result);
}
3.2.2 根据条件更新
QueryWrapper实体对象封装操作类,或可以为null
@Test
public void testUpdate1() {
User user = new User();
user.setUserName("jiangTao");//更新的字段
user.setPassword("loveCJ1314");
user.setMail("loveJT.com");
QueryWrapperwrapper = new QueryWrapper<>();///更新的条件
wrapper.eq("user_name", "jt");//匹配user_name=jt的数据
int result = userMapper.update(user, wrapper);
System.out.println("result=>" + result);
}
使用UpdateWrapper
@Test
public void testUpdate2() {
UpdateWrapperwrapper = new UpdateWrapper<>();
wrapper.set("age", 21).set("password", "loveChenJie1314")//设置更新字段
.eq("user_name", "jiangTao");//使用的都是字段的名字,不是实体中属性的名
int result = userMapper.update(null, wrapper);
System.out.println("result=>" + result);
}
3.3 删除操作
3.3.1 deleteById
@Test
public void testDeleteById() {
int result = userMapper.deleteById(9);
System.out.println("result=>" + result);
}
3.3.2 deleteByMap
@Test
public void testDeleteByMap() {
Mapmap = new HashMap<>();
map.put("user_name", "caocao");
map.put("id", 8);
//根据map查询数据,多条件之间为and关系
int result = this.userMapper.deleteByMap(map);
System.out.println("result=>" + result);
}
3.3.3 delete 根据包装条件删除
@Test
public void testDelete() {
//方法一:手动填写字段
// QueryWrapperwrapper = new QueryWrapper<>();
// wrapper.eq("user_name","caocao").eq("id",7);
//方法二:面向对象 ==推荐
User user = new User();
user.setUserName("caocao");
user.setId(6L);
QueryWrapperwrapper = new QueryWrapper<>(user);
int result = this.userMapper.delete(wrapper);
System.out.println("result=>" + result);
}
3.3.4 deleteBatchIds 根据id批量删除
@Test
public void testDeleteBatchIds(){
//Listlist = new ArrayList<>();
//list.add(15L);
//list.add(16L);
int result = this.userMapper.deleteBatchIds(Arrays.asList(10L, 11L));
System.out.println("result=>" + result);
}
3.4 查询操作
MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作。
3.4.1 selectById
@Test
public void testSelectById() {
User user = userMapper.selectById(4L);
System.out.println(user);
}
3.4.2 selectBatchIds
@Test
public void testSelectBatchIds(){
//根据id批量查询数据
Listusers = this.userMapper.selectBatchIds(Arrays.asList(4L,5L,1314L));
for (User user : users) {
System.out.println(user);
}
}
3.4.3 selectOne
@Test
public void testSelectOne(){
//根据条件查询一条语句,结果超过一条会报错
QueryWrapperwrapper = new QueryWrapper<>();
wrapper.eq("user_name","zhangsan");
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
3.4.4 selectCount
@Test
public void testSelectCount(){
//根据条件查询数据条数
QueryWrapperwrapper = new QueryWrapper<>();
wrapper.gt("age",20);//年龄大于20的用户
Integer count = this.userMapper.selectCount(wrapper);
System.out.println(count);
}
3.4.5 selectList
@Test
public void testSelectList(){
//根据条件查询
QueryWrapperwrapper = new QueryWrapper<>();
wrapper.like("email","test");//like模糊查询
Listusers = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
3.4.6、selectPage
配置分页插件:
@Configuration
@MapperScan("priv.cj.mapper") //设置mapper接口的扫描包
public class MybatisPlusConfig {
//配置分页拦截器,分页插件
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
@Test
public void testSelectPage(){
Pagepage = new Page<>(1,2);//查询第1页,查询2条数据
QueryWrapperwrapper = new QueryWrapper<>();
wrapper.notLike("email","test");
IPageiPage = this.userMapper.selectPage(page, wrapper);
System.out.println("数据总条数:"+iPage.getTotal());
System.out.println("数据总页数:"+iPage.getPages());
System.out.println("当前页数:"+iPage.getCurrent());
Listrecords = iPage.getRecords();//获取分页信息
for (User record : records) {
System.out.println(record);
}
}
3.5 SQL注入原理
MP在启动后会将BaseMapper中的一系列的方法注册到meppedStatements中,那么究竟是如何注入的呢?流程又是怎么样的?
-
在MP中,ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类
-
在AbstractSqlInjector中,主要是由inspectInject()方法进行注入的
-
在实现方法中, methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo)); 是关键,循环遍历方法,进行注入。
-
最终调用抽象方法injectMappedStatement进行真正的注入,该方法的实现:
以selectById查看:
可以看到,生成了SqlSource对象,再将SQL通过addSelectMappedStatement方法添加到meppedStatements中。
??配置
在MP中有大量的配置,其中有一部分是Mybatis原生的配置,另一部分是MP的配置,详情:https://mybatis.plus/config/ 下面我们对常用的配置做讲解。
4.1、基本配置
4.1.1、configLocation
MyBatis 配置文件位置,如果您有单独的 MyBatis 配置,请将其路径配置到 configLocation 中。
Spring MVC:
Spring Boot:
mybatis-plus.config-location = classpath:mybatis-config.xml
4.1.2、mapperLocations
MyBatis Mapper 所对应的 XML 文件位置,如果您在 Mapper 中有自定义方法(XML 中有自定义实现),需要进行该配置,告诉 Mapper 所对应的 XML 文件位置。
Spring MVC:
[tips] Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)
Spring Boot:
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml
4.1.3、typeAliasesPackage
MyBaits 别名包扫描路径.
Spring MVC:
Spring Boot:
mybatis-plus.type-aliases-package=priv.cj.pojo
4.2、进阶配置
本部分(Configuration)的配置大都为 MyBatis 原生支持的配置,这意味着您可以通过 MyBatis XML 配置文件的形 式进行配置。
4.2.1、mapUnderscoreToCamelCase
是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属 性名 aColumn(驼峰命名) 的类似映射。mor
SprinfBoot
# 关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在[tips] 此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body。如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
mybatis-plus.configuration.map-underscore-to-camel-case=false
4.2.2、cacheEnabled
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。
mybatis-plus.configuration.cache-enabled=false
4.3、DB 策略配置
4.3.1、idType
全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。
SpringBoot:
mybatis-plus.global-config.db-config.id-type=auto
SpringMVC:
4.3.2、tablePrefix
表名前缀,全局配置后可省略@TableName()配置。
SpringBoot:
mybatis-plus.global-config.db-config.table-prefix=tb_
SpringMVC:
??条件构造器
在MP中,Wrapper接口的实现类关系如下:
可以看到,AbstractWrapper和AbstractChainWrapper是重点实现,接下来我们重点学习AbstractWrapper以及其 子类
5.1、allEq
5.1.1 Map作为条件
allEq(Mapparams)
allEq(Mapparams, boolean null2IsNull)
allEq(boolean condition, Mapparams, boolean null2IsNull)
字段说明:
params : key 为数据库字段名, value 为字段值
null2IsNull : 为 true 则在 map 的 value 为 null 时调用 isNull 方法,为 false 时则忽略 value 为 null 的字段
测试用例:
@Test
public void testWrapper(){
Mapmap = new HashMap<>();
map.put("name","曹操");
map.put("age",21);
map.put("password",null);
QueryWrapperwrapper = new QueryWrapper<>();
//SELECT * FROM tb_user WHERE password IS NULL AND name = 曹操 AND age = 21
wrapper.allEq(map);
//SELECT * FROM tb_user WHERE name = 曹操 AND age = 21
wrapper.allEq(map,false);
Listusers = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
5.1.2 filter过滤函数
allEq(BiPredicatefilter, Map params)
allEq(BiPredicatefilter, Map params, boolean null2IsNull)
allEq(boolean condition, BiPredicatefilter, Map params, boolean null2IsNull)
字段说明:
filter : 过滤函数,是否允许字段传入比对
测试用例:
//SELECT * FROM tb_user WHERE name = 曹操 AND age = 21
wrapper.allEq((k, v) -> (k.equals("name") || k.equals("age")), map);
5.2、基本比较操作
| 方法名称 | 含义 |
|---|---|
| eq | 等于 = |
| ne | 不等于 <> |
| gt | 大于 > |
| ge | 大于等于 >= |
| lt | 小于 < |
| le | 小于等于 <= |
| between | BETWEEN 值1 AND 值2 |
| notBetween | NOT BETWEEN 值1 AND 值2 |
| in | 字段 IN (value.get(0), value.get(1), ...) |
QueryWrapperwrapper = new QueryWrapper<>();
wrapper.eq("password", "123456")
.ge("age", 20)
.in("name", "李四", "王五", "赵六");
//select * FROM tb_user WHERE password = ? AND age >= ? AND name IN (?,?,?)
Listusers = this.userMapper.selectList(wrapper);
5.3、模糊查询
| 方法名称 | 含义 |
|---|---|
| like | LIKE '%值%' |
| notLike | NOT LIKE '%值%' |
| likeLeft | LIKE '%值' |
| likeRight | LIKE '值%' |
5.4、排序
| 方法名称 | 含义 |
|---|---|
| orderBy | ORDER BY 字段, .. |
| orderByAsc | ORDER BY 字段, ... ASC |
| orderByDesc | ORDER BY 字段, ... DESC |
//select * FROM tb_user ORDER BY age ASC , id ASC
wrapper.orderBy(true,true,"age","id");
5.5、逻辑查询
| 方法名称 | 含义 |
|---|---|
| or | 用于拼接条件 OR |
| and | AND 嵌套 |
//SELECT * FROM tb_user WHERE name = ? OR age = ?
wrapper.eq("name","曹操").or().eq("age",12);
5.6、select
在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段。
//SELECT name FROM tb_user WHERE name = ? OR age = ?
wrapper.eq("name","曹操")
.or()
.eq("age",12)
.select("name");
Listusers = this.userMapper.selectList(wrapper);
?? ActiveRecord
ActiveRecord属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记 录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。
ActiveRecord的主要思想是:
-
每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;
-
通常表的每个字段 在类中都有相应的Field;
-
ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;
-
ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑;
6.1 开启AR之旅
在MP中,开启AR非常简单,只需要将实体对象继承Model即可。
@Data[tips] AR不需要注入UserMapper但UserMapper必须存在
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User extends Model{
@TableId(type = IdType.AUTO)//设置id自增长
private Long id;
private String userName;
@TableField(select = false)//查询时不返回当前字段中的值
private String password;
private String name;
private Integer age;
@TableField(value = "email")//解决字段名不一致
private String mail;
@TableField(exist = false)//解决对象字段在表中不存在的问题
private String favorite;
}
6.2 查询操作
@Test
public void testSelectById(){
User user = new User();
user.setId(2L);
//SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE id=?
User user1 = user.selectById();
System.out.println(user1);
}
@Test
public void testSelect(){
User user = new User();
QueryWrapperwrapper = new QueryWrapper<>();
wrapper.gt("age",18);
ListuserList = user.selectList(wrapper);
for (User user1 : userList) {
System.out.println(user1);
}
}
6.3 新增数据
@Test
public void testInsert(){
User user = new User();
user.setUserName("AR");
user.setPassword("123");
user.setAge(21);
user.setMail("AR.com");
boolean res = user.insert();
System.out.println(res);
}
6.4 更新操作
@Test
public void testUpdateById(){
User user = new User();
user.setId(12L);
user.setUserName("caocao");
user.setPassword("123123");
user.setAge(1);
user.setName("曹操");
boolean flag = user.updateById();
System.out.println("flag=>"+flag);
}
@Test
public void testUpdateByCondition(){
User user = new User();
user.setPassword("999");
user.setAge(99);
user.setName("枭雄");
QueryWrapperwrapper = new QueryWrapper<>();
wrapper.eq("user_name","caocao");
boolean flag = user.update(wrapper);
System.out.println("flag=>"+flag);
}
6.5 删除操作
@Test
public void testDelete(){
User user = new User();
user.setId(12L);
boolean flag = user.deleteById();
System.out.println("flag=>"+flag);
}
??Oracle 主键Sequence
- Oracle
??:插件
8.1、mybatis的插件机制
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法 调用包括:
-
Executor 拦截执行器的方法
-
ParameterHandler 拦截参数的处理
-
ResultSetHandler 拦截结果集的处理
-
StatementHandler 拦截Sql语法构建的处理
拦截器实例:
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class MyInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
//拦截方法,具体业务逻辑编写的位置
return invocation.proceed();
}
public Object plugin(Object target) {
//创建target对象的代理对象,目的是将当前拦截器加入到该对象中
return Plugin.wrap(target, (org.apache.ibatis.plugin.Interceptor) this);
}
public void setProperties(Properties properties) {
//属性设置
}
}
注入到Spring容器:
/**
* 自定义拦截器
*/
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
或者通过xml配置,mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
8.2、执行分析插件
在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,注意:该插件仅适用于开发环境,不 适用于生产环境。
SprinigBoot配置
@Bean[ttips] 当执行全表更新时,会抛出异常,这样有效防止了一些误操作。
public SqlExplainInterceptor sqlExplainInterceptor(){
SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
ListsqlParserList = new ArrayList<>();
// 攻击 SQL 阻断解析器、加入解析链
sqlParserList.add(new BlockAttackSqlParser());
sqlExplainInterceptor.setSqlParserList(sqlParserList);
return sqlExplainInterceptor;
}
8.3、性能分析插件
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常。
[^] 该插件只用于开发环境,不建议生产环境使用。配置:
<?xml version="1.0" encoding="UTF-8" ?>
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
8.4、乐观锁插件
当要更新一条记录的时候,希望这条记录没有被别人更新,可避免多人同时更新导致的数据不一致。
乐观锁实现方式:
-
取出记录时,获取当前version
-
更新时,带上这个version
-
执行更新时, set version = newVersion where version = oldVersion
-
如果version不对,就更新失败
8.4.1、配置:
Spring xml:
SpringBoot:
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
8.4.2、注解实体字段
第一步,为表添加version字段,并且设置初始值为1:
ALTER TABLE `tb_user`
ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `tb_user` SET `version`='1';
第二步,为User实体对象添加version字段,并且添加@Version注解:
@Version
private Integer version;
8.4.3、测试
@Test
public void testUpdate(){
User user = new User();
user.setAge(30);
user.setId(2L);
user.setVersion(1); //获取到version为1
int result = this.userMapper.updateById(user);
System.out.println("result = " + result);
}
??Sql注入器
在MP中,通过AbstractSqlInjector将BaseMapper中的方法注入到了Mybatis容器,这样这些方法才 可以正常执行。 那么,如果我们需要扩充BaseMapper中的方法,又该如何实现呢?
9.1、编写MyBaseMapper
public interface MyBaseMapperextends BaseMapper {
ListfindAll();
}
其他的Mapper都可以继承该Mapper,实现方法的扩展
public interface UserMapper extends MyBaseMapper{
User findById(Long id);
}
9.2、编写MySqlInjictor
如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的方法将失效,所以我们选择继承DefaultSqlInjector 进行扩展。
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List getMethodList() {
List methodList = super.getMethodList();
methodList.add(new FindAll());
// 再扩充自定义的方法
list.add(new FindAll());
return methodList;
}
}
9.3、编写FindAll
public class FindAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sqlMethod = "findAll";
String sql = "select * from " + tableInfo.getTableName();
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql,modelClass);
return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource,modelClass, tableInfo);
}
}
9.4、注册到Spring容器
/**
* 自定义SQL注入器
*/
@Bean
public MySqlInjector mySqlInjector(){
return new MySqlInjector();
}
??自动填充功能:
插入或者更新数据时,希望有些字段可以自动填充数据,比如默认密码、version 等。在MP中提供了这样的功能,可以实现自动填充。
10.1、添加@TableField注解
@TableField(fill = FieldFill.INSERT) //插入数据时进行填充
private String password;
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
10.2、编写MyMetaObjectHandler
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
Object password = getFieldValByName("password", metaObject);
if(null == password){
//字段为空,可以进行填充
setFieldValByName("password", "123456", metaObject);
}
}
@Override
public void updateFill(MetaObject metaObject) {
}
}
??逻辑删除
逻辑删除就是将数据标记为删除,而并非真正 的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免 数据被真正的删除。
11.1、修改表结构
为tb_user表增加deleted字段,用于表示数据是否被删除,1代表删除,0代表未删除。
ALTER TABLE `tb_user`
ADD COLUMN `deleted` int(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER`version`;
同时,也修改User实体,增加deleted属性并且添加@TableLogic注解:
@TableLogic
private Integer deleted;
11.2、配置
application.properties:
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
??通用枚举
解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!
12.1、修改表结构
ALTER TABLE `tb_user`ADD COLUMN `sex` int(1) NULL DEFAULT 1 COMMENT '1-男,2-女' AFTER `deleted`;
12.2、定义枚举
public enum SexEnum implements IEnum{
MAN(1,"男"),WOMAN(2,"女");
private int value;
private String desc;
SexEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return this.value;
}
@Override
public String toString() {
return "SexEnum{" +
"value=" + value +
", desc='" + desc + '\'' +
'}';
}
}
12.3、配置
# 枚举包扫描
mybatis-plus.type-enums-package=cn.itcast.mp.enums
12.4、修改实体
private SexEnum sex;
12.5、测试
@Test
public void testInsert(){
User user = new User();
user.setName("貂蝉");
user.setUserName("diaochan");
user.setAge(20);
user.setEmail("diaochan@itast.cn");
user.setVersion(1);
user.setSex(SexEnum.WOMAN);
int result = this.userMapper.insert(user);
System.out.println("result = " + result);
}
??代码生成器
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
13.1、创建工程
<?xml version="1.0" encoding="UTF-8"?>
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
cn.itcast.mp
itcast-mp-generator
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-test
test
com.baomidou
mybatis-plus-boot-starter
3.1.1
com.baomidou
mybatis-plus-generator
3.1.1
org.springframework.boot
spring-boot-starter-freemarker
mysql
mysql-connector-java
5.1.47
org.slf4j
slf4j-log4j12
org.springframework.boot
spring-boot-maven-plugin
13.2、代码
/**
*
* mysql 代码生成器演示例子
*
*/
public class MysqlGenerator {
/**
*
* 读取控制台内容
*
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
/**
* RUN THIS
*/
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("itcast");
gc.setOpen(false);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://127.0.0.1:3306/mp?
useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
pc.setParent("cn.itcast.mp.generator");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
ListfocList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输入文件名称
return projectPath + "/itcast-mpgenerator/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" +
StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
mpg.setTemplate(new TemplateConfig().setXml(null));
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//
strategy.setSuperEntityClass("com.baomidou.mybatisplus.samples.generator.common.BaseEntity");
strategy.setEntityLombokModel(true);
//
strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.common.BaseController");
strategy.setInclude(scanner("表名"));
strategy.setSuperEntityColumns("id");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
// 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
13.3、测试
生成代码
?? MybatisX 快速开发插件
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。
功能:
-
Java 与 XML 调回跳转
-