任何能使用
MyBatis
进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。
使用spring initalize 快速初始化一个spring boot工程
pom.xml配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.4version>
<relativePath/>
parent>
<groupId>indi.yuluogroupId>
<artifactId>mybatisplusartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>mybatisplusname>
<description>mybatisplusdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.zaxxergroupId>
<artifactId>HikariCPartifactId>
<version>3.3.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
简化实体类的开发
使用@Data
注解来完成实体类的有参和无参构造,hasCode和equals以及toStirng和getSet方法
spring:
# 配置数据源信息
datasource:
# 配置数据源类型
type: com.zaxxer.hikari.HikariDataSource
# 配置连接数据库的各个信息
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?severTimezone=GMT%2B8&characterEncoding=utf-8&ssl=false
username: root
password: root
@Repository 标记为持久层组件 解决测试类中的引用报红线问题
@MapperScan 扫描到的mapper。并不是将接口交给IOC容器进行管理,而是将UserMapper动态生成的代理类交由IOC容器管理,所以IDEA在编译的时候认为UserMapper无法进行自动装配。在运行阶段的时候是没有这个错误的
package indi.yuluo.mybatisplus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import indi.yuluo.mybatisplus.pojo.User;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper {
}
package indi.yuluo.mybatisplus;
import indi.yuluo.mybatisplus.mapper.UserMapper;
import indi.yuluo.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author: yuluo
* @createTime: 2022/3/15 22:30
* @File: MyBatisPlusTest.java
* @Software: IntelliJ IDEA
* @Description:
*/
@SpringBootTest
public class MyBatisPlusTest {
@Autowired
private UserMapper userMapper;
/**
* 用于测试:查询集合的方法
*/
@Test
public void testSelectList() {
// 通过条件构造器来查询一个list集合,若没有条件,则可以设置null为参数
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
}
查看生成的sql语句
spring:
# 配置数据源信息
datasource:
# 配置数据源类型
type: com.zaxxer.hikari.HikariDataSource
# 配置连接数据库的各个信息
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?severTimezone=GMT%2B8&characterEncoding=utf-8&ssl=false
username: root
password: w082916x
mybatis-plus:
configuration:
# mybatis-plus日志配置
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus会先去扫描相对应的实体类,根据反射抽取出来里面的属性名,然后在分析要操作的表是谁,属性名(表中的字段)是谁,生成相对应的sql语句来交给IMC容器进行查询
/**
* 用于测试:BaseMapper里面的insert方法
*/
@Test
public void testInsert() {
// 准备参数
User user = new User();
user.setAge(20);
user.setName("yuluo");
user.setEmail("[email protected]");
// 调用insert方法插入 返回值为受影响的行数
int insert = userMapper.insert(user);
System.out.println("insert = " + insert);
// 获取生成的id
System.out.println("id : " + user.getId());
}
执行的SQL语句
==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
==> Parameters: 1504020449580343298(Long), yuluo(String), 20(Integer), [email protected](String)
<== Updates: 1
/**
* 用于测试:BaseMapper的删除方法
* 加L表示long类型的数据
*/
@Test
public void test() {
int i = userMapper.deleteById(1504020449580343298L);
System.out.println("i = " + i);
// 准备删除的条件
// 根据map集合中设置的条件来删除用户信息
Map<String, Object> map = new HashMap<>();
map.put("name", "yuluo");
map.put("age", 20);
userMapper.deleteByMap(map);
// 通过多个id来进行批量删除
List<Long> lists = Arrays.asList(1L, 2L);
userMapper.deleteBatchIds(lists);
}
==> Preparing: DELETE FROM user WHERE id=?
==> Parameters: 1504020449580343298(Long)
<== Updates: 1
==> Preparing: DELETE FROM user WHERE name = ? AND age = ?
==> Parameters: yuluo(String), 20(Integer)
<== Updates: 0
==> Preparing: DELETE FROM user WHERE id IN ( ? , ? )
==> Parameters: 1(Long), 2(Long)
<== Updates: 2
/**
* 用于测试:BaseMapper里面的修改方法
*/
@Test
public void testUpdate() {
// 修改用户信息
User user = new User();
user.setId(3L);
user.setName("huakai");
user.setEmail("[email protected]");
int i = userMapper.updateById(user);
System.out.println("i = " + i);
}
==> Preparing: UPDATE user SET name=?, email=? WHERE id=?
==> Parameters: huakai(String), [email protected](String), 3(Long)
<== Updates: 1
/**
* 用于测试:BaseMapper里的查询方法
*/
@Test
public void testSelect() {
// 通过id来查询用户信息
User user = userMapper.selectById(3L);
System.out.println("user = " + user);
// 通过id的集合来批量查询
List<Long> longs = Arrays.asList(3L, 4L);
List<User> list = userMapper.selectBatchIds(longs);
list.forEach(System.out::println);
// 根据map集合里的条件来查询用户信息
Map<String, Object> map = new HashMap<>();
map.put("name", "huakai");
map.put("age", "28");
List<User> list = userMapper.selectByMap(map);
list.forEach(System.out::println);
}
==> Preparing: SELECT id,name,age,email FROM user WHERE id=?
==> Parameters: 3(Long)
<== Columns: id, name, age, email
<== Row: 3, huakai, 28, [email protected]
<== Total: 1
==> Preparing: SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
==> Parameters: 3(Long), 4(Long)
<== Columns: id, name, age, email
<== Row: 3, huakai, 28, [email protected]
<== Row: 4, Sandy, 21, [email protected]
<== Total: 2
==> Preparing: SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
==> Parameters: huakai(String), 28(String)
<== Columns: id, name, age, email
<== Row: 3, huakai, 28, [email protected]
<== Total: 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vn4P1mfj-1647525489169)(images/image-20220316174729446-16474240543151.png)]
package indi.yuluo.mybatisplus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import indi.yuluo.mybatisplus.pojo.User;
import org.springframework.stereotype.Repository;
import java.util.Map;
// 标记为持久层组件 解决测试类中的引用报红线问题
@Repository
public interface UserMapper extends BaseMapper<User> {
/**
* 根据id查询用户信息为map集合
* @param id
* @return
*/
Map<String, Object> selectById(Long id);
}
<mapper namespace="indi.yuluo.mybatisplus.mapper.UserMapper">
<select id="selectById" resultType="map">
select id, name, age, email from user where id=#{id}
select>
mapper>
/**
* 用于测试:自定义查询功能
*/
@Test
public void test() {
Map<String, Object> map = userMapper.selectById(4L);
System.out.println(map);
}
通用 Service CRUD 封装IService (opens new window)接口,进一步封装 CRUD 采用 get 查询单行
remove 删除
list 查询集合
page 分页
前缀命名方式区分 Mapper
层避免混淆,
泛型 T
为任意实体对象
建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService
继承 Mybatis-Plus
提供的基类
对象 Wrapper
为 条件构造器
MyBatis-Plus中有一个接口IService接口和其实现类ServiceImpl,封装了常见的业务层逻辑
package indi.yuluo.mybatisplus.service;
import com.baomidou.mybatisplus.extension.service.IService;
import indi.yuluo.mybatisplus.pojo.User;
public interface UserService extends IService<User> {
}
package indi.yuluo.mybatisplus.service.Impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import indi.yuluo.mybatisplus.mapper.UserMapper;
import indi.yuluo.mybatisplus.pojo.User;
import indi.yuluo.mybatisplus.service.UserService;
import org.springframework.stereotype.Service;
/**
* @author: yuluo
* @createTime: 2022/3/16 18:02
* @File: UserServieImpl.java
* @Software: IntelliJ IDEA
* @Description:
*/
@Service
public class UserServieImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
package indi.yuluo.mybatisplus;
import indi.yuluo.mybatisplus.mapper.UserMapper;
import indi.yuluo.mybatisplus.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author: yuluo
* @createTime: 2022/3/16 18:13
* @File: MyBatisPlusServiceTest.java
* @Software: IntelliJ IDEA
* @Description:
*/
@SpringBootTest
public class MyBatisPlusServiceTest {
// 自动装配
@Autowired
private UserService userService;
/**
* 用于测试:
*/
@Test
public void testGetCount() {
// 查询总记录数
long count = userService.count();
System.out.println("count = " + count);
}
}
==> Preparing: SELECT COUNT( * ) FROM user
==> Parameters:
<== Columns: COUNT( * )
<== Row: 3
<== Total: 1
/**
* 用于测试:通用service的批量添加操作
*/
@Test
public void testInsertMore() {
// 准备数据
// 批量添加
List<User> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
User user = new User();
user.setName("yuluo" + i);
user.setAge(20 + i);
list.add(user);
}
boolean b = userService.saveBatch(list);
System.out.println("b = " + b);
}
==> Preparing: INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? )
==> Parameters: 1504040927478484993(Long), yuluo0(String), 20(Integer)
==> Parameters: 1504040927553982465(Long), yuluo1(String), 21(Integer)
==> Parameters: 1504040927553982466(Long), yuluo2(String), 22(Integer)
==> Parameters: 1504040927553982467(Long), yuluo3(String), 23(Integer)
==> Parameters: 1504040927553982468(Long), yuluo4(String), 24(Integer)
==> Parameters: 1504040927553982469(Long), yuluo5(String), 25(Integer)
==> Parameters: 1504040927553982470(Long), yuluo6(String), 26(Integer)
==> Parameters: 1504040927553982471(Long), yuluo7(String), 27(Integer)
==> Parameters: 1504040927553982472(Long), yuluo8(String), 28(Integer)
==> Parameters: 1504040927553982473(Long), yuluo9(String), 29(Integer)
b = true
表名由BaseMapper的泛型类型决定
package indi.yuluo.mybatisplus.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: yuluo
* @createTime: 2022/3/15 22:19
* @File: User.java
* @Software: IntelliJ IDEA
* @Description:
*/
@Data
@TableName("t_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
当所有的表名前面都有一个一致的前缀的时候,可以在yml配置文件中通过配置给所有的实体类加上同一个前缀
mybatis-plus:
configuration:
# mybatis-plus日志配置
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 设置MyBatis-Plus的全局配置
global-config:
db-config:
# 设置实体类所对应的表的统一前缀
table-prefix: t_
将属性所对应的字段指定为主键
package indi.yuluo.mybatisplus.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: yuluo
* @createTime: 2022/3/15 22:19
* @File: User.java
* @Software: IntelliJ IDEA
* @Description:
*/
@Data
@TableName("t_user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
}
作用:用于指定主键的字段
package indi.yuluo.mybatisplus.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: yuluo
* @createTime: 2022/3/15 22:19
* @File: User.java
* @Software: IntelliJ IDEA
* @Description:
*/
@Data
@TableName("t_user")
public class User {
// value可以省略
@TableId(value="uId")
private Long id;
private String name;
private Integer age;
private String email;
}
package indi.yuluo.mybatisplus.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: yuluo
* @createTime: 2022/3/15 22:19
* @File: User.java
* @Software: IntelliJ IDEA
* @Description:
*/
@Data
@TableName("t_user")
public class User {
@TableId(value = "uId", type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}
spring:
# 配置数据源信息
datasource:
# 配置数据源类型
type: com.zaxxer.hikari.HikariDataSource
# 配置连接数据库的各个信息
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?severTimezone=GMT%2B8&characterEncoding=utf-8&ssl=false
username: root
password: w082916x
mybatis-plus:
configuration:
# mybatis-plus日志配置
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 设置MyBatis-Plus的全局配置
global-config:
db-config:
# 设置实体类所对应的表的统一前缀
table-prefix: t_
# 设置统一的主键生成策略
id-type: auto
指定属性对应的字段名
@TableField("user_name")
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”。此后在数据库中任然可以看见此条数据记录
使用场景:可以进行数据恢复
逻辑删除的实现步骤:
@TableLogic
注解package indi.yuluo.mybatisplus.pojo;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
/**
* @author: yuluo
* @createTime: 2022/3/15 22:19
* @File: User.java
* @Software: IntelliJ IDEA
* @Description:
*/
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
@TableLogic
private Integer isDeleted;
}
需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量
数据库的扩展方式主要包括:业务分库,主从复制,数据库分表
将不同业务数据分撒存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈
雪花算法是由Twitier公布的分布式主键生成算法,能够保证不同表的主键的不重复性,以及相同表的主键的有序性
/**
* 用于测试:wrapper条件构造器
*/
@Test
public void test1() {
// 查询条件: 用户名包含a,年两在20到30之间并且邮箱不为空的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a")
.between("age", 20, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::print);
}
==> Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
==> Parameters: %a%(String), 20(Integer), 30(Integer)
<== Columns: id, name, age, email, is_deleted
<== Row: 3, huakai, 28, [email protected], 0
<== Row: 4, Sandy, 21, [email protected], 0
<== Total: 2
/**
* 用于测试:wrapper条件构造器组装排序条件
*/
@Test
public void test2() {
// 查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age")
.orderByAsc("id");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::print);
}
==> Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 ORDER BY age DESC,id ASC
==> Parameters:
<== Columns: id, name, age, email, is_deleted
<== Row: 1504040927553982473, yuluo9, 29, null, 0
<== Row: 3, huakai, 28, [email protected], 0
<== Row: 1504040927553982472, yuluo8, 28, null, 0
<== Row: 1504040927553982471, yuluo7, 27, null, 0
<== Row: 1504040927553982470, yuluo6, 26, null, 0
<== Row: 1504040927553982469, yuluo5, 25, null, 0
<== Row: 5, Biilie, 24, [email protected], 0
<== Row: 1504040927553982468, yuluo4, 24, null, 0
<== Row: 1504040927553982467, yuluo3, 23, null, 0
<== Row: 1504040927553982466, yuluo2, 22, null, 0
<== Row: 4, Sandy, 21, [email protected], 0
<== Row: 1504040927553982465, yuluo1, 21, null, 0
<== Row: 1504040927478484993, yuluo0, 20, null, 0
<== Total: 13
/**
* 用于测试:wrapper条件构造器组装删除条件
*/
@Test
public void test3() {
// 删除所有邮箱为null的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int delete = userMapper.delete(queryWrapper);
// 输出受影响的行数
System.out.println("delete = " + delete);
}
因为加入了逻辑删除字段,所以删除时只是将is_deleted更改为了1
sql语句
==> Preparing: UPDATE user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)
==> Parameters:
<== Updates: 10
/**
* 用于测试:wrapper条件构造器组装修改条件
*/
@Test
public void test() {
// 将(年龄大于20并且用户名中包含a)或邮箱为nul来的用户信息修改
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 当条件是and关系是不需要写出and 默认给出and
// 当条件之间是or关系时,需要用or()来连接
queryWrapper.gt("age", 20)
.like("name", "a")
. or()
.isNull("email");
// 设置修改的属性
User user = new User();
user.setName("huakai");
user.setEmail("[email protected]");
// update()两个参数一个设置的是修改的内容,一个是wrapper条件
int update = userMapper.update(user, queryWrapper);
System.out.println("update = " + update);
}
==> Preparing: UPDATE user SET name=?, email=? WHERE is_deleted=0 AND (age > ? AND name LIKE ? OR email IS NULL)
==> Parameters: huakai(String), [email protected](String), 20(Integer), %a%(String)
<== Updates: 2
确保条件优先执行的时候可以执行and方法
lambda中的条件优先执行
/**
* 用于测试:wrapper条件构造器的条件优先级
*/
@Test
public void test5() {
// 将用户名中包含有a并且(年龄大于10或者邮箱为null)的用户信息修改
// lambda中的条件优先执行
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a")
.and(i -> i.gt("age", 10).or().isNull("email"));
// 设置修改的属性
User user = new User();
user.setName("huakai love yuluo");
user.setEmail("[email protected]");
int update = userMapper.update(user, queryWrapper);
System.out.println("update = " + update);
}
==> Preparing: UPDATE user SET name=?, email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
==> Parameters: huakai love yuluo(String), [email protected](String), %a%(String), 10(Integer)
<== Updates: 2
/**
* 用于测试:组装select子句
*/
@Test
public void test6() {
// 查询用户的用户名、年龄、邮箱信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age", "email");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
==> Preparing: SELECT name,age,email FROM user WHERE is_deleted=0
==> Parameters:
<== Columns: name, age, email
<== Row: huakai love yuluo, 28, [email protected]
<== Row: huakai love yuluo, 21, [email protected]
<== Row: Biilie, 24, [email protected]
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4cb40e3b]
// 查询结果
{name=huakai love yuluo, age=28, [email protected]}
{name=huakai love yuluo, age=21, [email protected]}
{name=Biilie, age=24, [email protected]}
/**
* 用于测试:组装子查询
*/
@Test
public void test7() {
// 查询id小于等于100的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lt("id", 100);
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
==> Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (id < ?)
==> Parameters: 100(Integer)
<== Columns: id, name, age, email, is_deleted
<== Row: 3, huakai love yuluo, 28, [email protected], 0
<== Row: 4, huakai love yuluo, 21, [email protected], 0
<== Row: 5, Biilie, 24, [email protected], 0
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6cd56321]
//
User(id=3, name=huakai love yuluo, age=28, [email protected], isDeleted=0)
User(id=4, name=huakai love yuluo, age=21, [email protected], isDeleted=0)
User(id=5, name=Biilie, age=24, [email protected], isDeleted=0)
子查询SQL语句
select * from user where id in (select id from user where id <= 100);
/**
* 用于测试:组装子查询
*/
@Test
public void test7() {
// 查询id小于等于100的用户信息
/*QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.lt("id", 100);
List list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);*/
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id <= 100");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
ELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (id IN (select id from user where id <= 100))
==> Parameters:
<== Columns: id, name, age, email, is_deleted
<== Row: 3, huakai love yuluo, 28, [email protected], 0
<== Row: 4, huakai love yuluo, 21, [email protected], 0
<== Row: 5, Biilie, 24, [email protected], 0
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@19382338]
User(id=3, name=huakai love yuluo, age=28, [email protected], isDeleted=0)
User(id=4, name=huakai love yuluo, age=21, [email protected], isDeleted=0)
User(id=5, name=Biilie, age=24, [email protected], isDeleted=0)
/**
* 用于测试:updateWrapper实现修改功能
*/
@Test
public void test8() {
// 将用户名中包含有a并且(年龄大于20或者邮箱为null)的用户信息修改
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.like("user_name", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
// 设置修改内容
updateWrapper.set("name", "yuluo").set("email", "[email protected]");
int update = userMapper.update(null, updateWrapper);
System.out.println("update = " + update);
}
==> Preparing: UPDATE user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
==> Parameters: yuluo(String), [email protected](String), %a%(String), 20(Integer)
<== Updates: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c15e8c7]
update = 2
/**
* 用于测试:模拟实际开发中的场景
*/
@Test
public void test9() {
String name = "";
Integer ageBegin = 20;
Integer ageEnd = 30;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 组装条件
if (StringUtils.isNotBlank(name)) {
// isNotBlank判断某个字符串是否不为空字符串,不为null,不为空白符
queryWrapper.like("name", name);
}
if (ageBegin != null) {
queryWrapper.ge("age", ageBegin);
}
if (ageEnd != null) {
queryWrapper.lt("age", ageEnd);
}
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
==> Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (age >= ? AND age < ?)
==> Parameters: 20(Integer), 30(Integer)
<== Columns: id, name, age, email, is_deleted
<== Row: 3, yuluo, 28, [email protected], 0
<== Row: 4, yuluo, 21, [email protected], 0
<== Row: 5, Biilie, 24, [email protected], 0
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5707f613]
User(id=3, name=yuluo, age=28, [email protected], isDeleted=0)
User(id=4, name=yuluo, age=21, [email protected], isDeleted=0)
User(id=5, name=Biilie, age=24, [email protected], isDeleted=0)
/**
* 用于测试:模拟实际开发中的场景 简便方法
*/
@Test
public void test10() {
String name = "a";
Integer ageBegin = 20;
Integer ageEnd = 30;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(name), "name", name)
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
/**
* 用于测试:测试LambdaQueryWrapper 同test10
*/
@Test
public void test11() {
String name = "a";
Integer ageBegin = 20;
Integer ageEnd = 30;
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 使用钻石表达式来代替数据库字段名,防止写错
lambdaQueryWrapper.like(StringUtils.isNotBlank(name), User::getName, name)
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List<User> list = userMapper.selectList(lambdaQueryWrapper);
list.forEach(System.out::println);
}
/**
* 用于测试:updateWrapper实现修改功能
*/
@Test
public void test8() {
// 将用户名中包含有a并且(年龄大于20或者邮箱为null)的用户信息修改
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.like(User::getName, "a")
.and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));
// 设置修改内容
updateWrapper.set(User::getName, "yuluo").set(User::getEmail, "[email protected]");
int update = userMapper.update(null, updateWrapper);
System.out.println("update = " + update);
}
有了配置类之后,建议MapperScan注解从启动类移动到配置类上
package indi.yuluo.mybatisplus.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: yuluo
* @createTime: 2022/3/17 17:18
* @File: MyBatisPlusConfig.java
* @Software: IntelliJ IDEA
* @Description:
*/
@Configuration
// 扫描mapper接口所在的包
@MapperScan("indi.yuluo.mybatisplus.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
/**
* 用于测试:
*/
@Test
public void testPage() {
// mysql数据库使用的是limit关键字来进行分页操作
// limit后面的两个参数是页码和展示的记录条数
Page<User> page = new Page<User>(1, 3);
userMapper.selectPage(page, null);
System.out.println(page);
}
==> Preparing: SELECT COUNT(*) AS total FROM user WHERE is_deleted = 0
==> Parameters:
<== Columns: total
<== Row: 3
<== Total: 1
==> Preparing: SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 LIMIT ?
==> Parameters: 3(Long)
<== Columns: id, name, age, email, is_deleted
<== Row: 3, yuluo, 28, [email protected], 0
<== Row: 4, yuluo, 21, [email protected], 0
<== Row: 5, Biilie, 24, [email protected], 0
<== Total: 3
getPages(); 获取当前页的页码
getRecords(); 获取当前页的记录
getTotal(); 获取总记录数
hasNext(); 判断有没有下一页
hasPrevious(); 判断有没有上一页
/**
* 通过年龄查询用户信息并分页
* @param page MyBatis-Plus提供的分页插件,必须位于第一个参数的位置
* @param age
* @return
*/
Page<User> selectPageVo(@Param("page")Page<User> page,
@Param("age") Integer age);
<select id="selectPageVo" resultType="User">
select id, name, age, email from user where age > #{age}
select>
# 配置类型别名所对应的包
type-aliases-package: indi.yuluo.mybatisplus.pojo
/**
* 用于测试:自定义的分页功能
*/
@Test
public void testPageVo() {
Page<User> page = new Page<User>(1, 3);
userMapper.selectPageVo(page, 20);
System.out.println(page);
}
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量
乐观锁通过 版本号 来实现
当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制【Pessimistic Concurrency Control,缩写“PCC”,又名“悲观锁”】
package indi.yuluo.mybatisplus;
import indi.yuluo.mybatisplus.mapper.ProductMapper;
import indi.yuluo.mybatisplus.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author: yuluo
* @createTime: 2022/3/17 19:51
* @File: ProductTest.java
* @Software: IntelliJ IDEA
* @Description:
*/
@SpringBootTest
public class ProductTest {
@Autowired
private ProductMapper productMapper;
/**
* 用于测试:product表的测试类
*/
@Test
public void testProduct01() {
// 小李查询商品价格
Product productLi = productMapper.selectById(1);
System.out.println("小李查询到的商品价格:" + productLi.getPrice());
// 小明查询商品价格
Product productWang = productMapper.selectById(1);
System.out.println("小王查询到的商品价格:" + productWang.getPrice());
// 小李将商品价格提高50
productLi.setPrice(productLi.getPrice() + 50);
productMapper.updateById(productLi);
// 小王将商品的价格减去30
productWang.setPrice(productWang.getPrice() - 10);
productMapper.updateById(productWang);
// 老板查询商品价格
Product productLaoban = productMapper.selectById(1);
System.out.println("老板查询到的商品价格:" + productLaoban.getPrice());
}
}
在实体类中版本号属性上添加@Version // 标识乐观锁版本号字段
注解
在配置Bean中添加插件
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CwHaxe0N-1647525489170)(images/image-20220317201837848-16475195286431.png)]
因为小李在修改了商品的价格之后,商品的版本号发生了变化,所以小王修改失败了
优化修改流程
// 小李将商品价格提高50
productLi.setPrice(productLi.getPrice() + 50);
productMapper.updateById(productLi);
// 小王将商品的价格减去30
productWang.setPrice(productWang.getPrice() - 10);
int i = productMapper.updateById(productWang);
// 版本号发生了变化之后再次进行修改操作
if (i == 0) {
Product productNew = productMapper.selectById(1);
productNew.setPrice(productNew.getPrice() - 30);
productMapper.updateById(productNew);
}
// 老板查询商品价格
Product productLaoban = productMapper.selectById(1);
System.out.println("老板查询到的商品价格:" + productLaoban.getPrice());
老板查询到的商品价格:120
最终的结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sAas9wmN-1647525489170)(images/image-20220317202549832-16475199510892.png)]
package indi.yuluo.mybatisplus.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
/**
* @author: yuluo
* @createTime: 2022/3/17 20:28
* @File: SexEnum.java
* @Software: IntelliJ IDEA
* @Description:
*/
@Getter
public enum SexEnum {
MALE(1, "男"),
FEMALE(2, "女");
// 注解的作用是将标识的属性的值存放到数据库中
@EnumValue
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
package indi.yuluo.mybatisplus;
import indi.yuluo.mybatisplus.enums.SexEnum;
import indi.yuluo.mybatisplus.mapper.UserMapper;
import indi.yuluo.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author: yuluo
* @createTime: 2022/3/17 21:24
* @File: MyBatisPlusEnumTest.java
* @Software: IntelliJ IDEA
* @Description:
*/
@SpringBootTest
public class MyBatisPlusEnumTest {
@Autowired
private UserMapper userMapper;
/**
* 用于测试:
*/
@Test
public void test() {
User user = new User();
user.setAge(20);
user.setEmail("[email protected]");
user.setName("huakai");
user.setSex(SexEnum.FEMALE);
int insert = userMapper.insert(user);
System.out.println("insert = " + insert);
}
}
# 扫描通用枚举的包
type-enums-package: indi.yuluo.mybatisplus.enums
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>org.freemarkergroupId>
<artifactId>freemarkerartifactId>
<version>2.3.31version>
dependency>
package indi.yuluo.mybatisplus;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Collections;
/**
* @author: yuluo
* @createTime: 2022/3/17 21:39
* @File: MyBatisPlusFastAutoGeneratorTest.java
* @Software: IntelliJ IDEA
* @Description:
*/
@SpringBootTest
public class MyBatisPlusFastAutoGeneratorTest {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf-8&userSSL=false", "root", "w082916x")
// 全局配置
.globalConfig(builder -> {
builder.author("yuluo") // 设置作者
// .enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("F://mybatis-plus"); // 指定输出目录
})
// 包配置
.packageConfig(builder -> {
builder.parent("indi.yuluo") // 设置父包名
.moduleName("mybatisplus") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "F://mybatis-plus")); // 设置mapperXml生成路径
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude("user") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
tor.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Collections;
/**
@SpringBootTest
public class MyBatisPlusFastAutoGeneratorTest {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf-8&userSSL=false", "root", "w082916x")
// 全局配置
.globalConfig(builder -> {
builder.author("yuluo") // 设置作者
// .enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("F://mybatis-plus"); // 指定输出目录
})
// 包配置
.packageConfig(builder -> {
builder.parent("indi.yuluo") // 设置父包名
.moduleName("mybatisplus") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "F://mybatis-plus")); // 设置mapperXml生成路径
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude("user") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}