第一章 MyBatis-Plus入门
1)MP简介
- 官方地址: http://mp.baomidou.com
- 代码发布地址: Github: https://github.com/baomidou/mybatis-plus
- Gitee: https://gitee.com/baomidou/mybatis-plus
- 文档发布地址: https://baomidou.com/pages/24112f
2)开发环境准备
第一步 pom.xml导入依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
第二步 yml配置数据源:
-
驱动类spring boot 2.0(内置jdbc5驱动),驱动类使用:driver-class-name:
com.mysql.jdbc.Driver
驱动类spring boot 2.1及以上(内置jdbc8驱动),驱动类使用: driver-class-name:
com.mysql.cj.jdbc.Driver
否则运行测试用例的时候会有 WARN 信息 -
连接地址url MySQL5.7版本的url:
jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
MySQL8.0版本的url:
serverTimezone是时区配置,可以选择
UTC
和Asia/Shanghai
或如下写法等。jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
否则运行测试用例报告如下错误: java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more
# 配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
# ?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # 中国上海
# ?serverTimezone=UTC # 协调世界时
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
username: root
password: root
第三步 造pojo和dao:
实体类domain:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
private Long id;
private String title;
private String author;
private String createTime;
private Integer views;
}
dao/mapper:
BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的 实体类型
BaseMapper源码地址:点击打开文件夹
public interface UserMapper extends BaseMapper<User> {
}
在启动类中注册:@MapperScan("com.yang.dao")
@SpringBootApplication
//扫描mapper接口所在的包
@MapperScan("com.yang.dao")
public class Springboot0103MybatisplusApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot0103MybatisplusApplication.class, args);
}
}
tips:快速复制包地址
第四步 测试:
@SpringBootTest(classes = Springboot0103MybatisplusApplication.class)
public class BlogDaoTest {
@Autowired
private BlogDao blogDao;
@Test
public void getBlogById(){
System.out.println( blogDao.selectById(1));
List<Blog> list = blogDao.selectList(null);
//使用list中的forEach方法
list.forEach(System.out::println);
}
}
其他设置:
# 使用mp自带的功能,打印日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3)CRUD
3.1)BaseMapper的CRUD
也就是直接通过dao层(mapper层)继承BaseMapper
接口进行基本CRUD
增:
MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id
@Test
public void insertBlog(){
Blog blog = new Blog();
blog.setTitle("操作系统");
blog.setAuthor("张三");
blog.setCreateTime(new Date());
blog.setViews(9999);
int result = blogDao.insert(blog);
System.out.println(result);
}
删:
-
通过id删除记录:
@Test public void testDeleteById(){ //根据id删除 //DELETE FROM blog WHERE id=? blogDao.deleteById(6); }
-
通过id批量删除:
@Test public void testDeleteBatchIds(){ //通过id批量删除记录 //DELETE FROM blog WHERE id IN ( ? , ? , ? ) List<Long> listId = Arrays.asList(3L,4L,5L); //因为数据类型是Long 所以要加L int result = ublogDao.deleteBatchIds(listId); System.out.println("受影响行数:"+result); }
-
通过map条件删除记录
@Test public void deleteBlog(){ //根据map集合中所设置的条件删除记录 //DELETE FROM blog WHERE title = ? AND author = ? Map<String,Object> map = new HashMap<>(); map.put("title","javaSE"); map.put("author","杨昱杰"); blogDao.deleteByMap(map); }
改:
@Test
public void updateBlog(){
//UPDATE blog SET title=?, author=?, create_time=?, views=? WHERE id=?
Blog blog = new Blog(1566338320572059649L, "稀里糊涂", "佚名", new Date(), 666);
System.out.println("受影响的行数:\t"+blogDao.updateById(blog));
}
查:
默认方式
-
通过map条件查询用户信息&根据多个id查询多个用户信息
-
根据
id查询
和全部查询
请参考如上@Test public void testSelectByMap(){ //通过map条件查询用户信息 //SELECT * FROM blog WHERE title = ? AND author = ? Map<String, Object> map = new HashMap<>(); map.put("title", "操作系统"); map.put("author", "张三"); List<User> list = userMapper.selectByMap(map); list.forEach(System.out::println); //根据多个id查询多个用户信息 //SELECT id,name,age,email FROM user WHERE id IN ( ? , ? ) List<Long> idList = Arrays.asList(4L, 5L); List<User> list = userMapper.selectBatchIds(idList); list.forEach(System.out::println); }
测试自定义功能
第一步 dao/mapper
@Repository
public interface BlogDao extends BaseMapper<Blog> {
/**
* 根据id查询用户信息为map集合
* @param id
* @return
*/
public Map<String,Object> selectBlogByMapId(Long id);
}
第二步 mybatis-mapper.xml :
注意事项:
mybatis-mapper.xml
必须放在resources
目录下的mappe
r文件夹下
<?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="com.yang.dao.BlogDao">
<select id="selectBlogByMapId" resultType="map">
select id,title,author,create_time,views from blog where id=#{id};
</select>
</mapper>
第三步 测试
@Test
public void selectBlogByMapIdTest(){
System.out.println(blogDao.selectBlogByMapId(2L));
}
tips: IDEA自定义mybats映射文件(xml)模板
mapper 模板:
这里特别注意:
namespace
需指定数据层(dao)中的接口类<?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=""> </mapper>
补充:这里不需要核心配置文件
cfg 模板(核心配置文件):
<?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> </configuration>
3.2)Service CRUD 接口⭐
3.2.1)常用接口开头参数:
参数开头 | 行为 |
---|---|
Save | 添加 & 修改 (带Id) |
Remove | 删除 |
Update | 修改 |
Get | 根据条件查询 |
List | 查询所有或多个 |
Page | 分页 |
Count | 数据条数 |
说明:
- 通用 Service CRUD 封装IService 接口,进一步封装 CRUD 采用
get 查询单行
remove 删除
list 查询集合
page 分页
前缀命名方式区分Mapper
层避免混淆,- 泛型
T
为任意实体对象- 建议如果存在自定义通用 Service 方法的可能,请创建自己的
IBaseService
继承Mybatis-Plus
提供的基类- 对象
Wrapper
为条件构造器
MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑 详情查看源码IService和ServiceImpl
3.2.2)实现
第一步 创建Service接口和实现类:
service层接口:
注意:要继承
IService<T>
/**
* UserService继承IService模板提供的基础功能
*/
public interface BlogService extends IService<Blog> {
}
service层impl实现类:
注意:IService有自己的实现类
ServiceImpl<M extends BaseMapper<T>, T>
,所以service层实现类可以先继承ServiceIpml
再实现自己的BlogService
接口
/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现
* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
*/
@Service
public class BlogServiceImpl extends ServiceImpl<BlogDao, Blog> implements BlogService {
}
/**
=====> dao层(就是接着上面写的dao) <=====
@Repository
public interface BlogDao extends BaseMapper<Blog> {
}
*/
第二步 测试:
@SpringBootTest
public class BlogServiceTest {
@Autowired
private BlogService service;
@Test
void testGetCount(){
//查询有多少条数据:SELECT COUNT( * ) FROM blog
System.out.println("总共有:"+service.count()+" 条数据");
//批量添加:INSERT INTO blog ( id, title, author, create_time, views ) VALUES ( ?, ?, ?, ?, ? )
List<Blog> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Blog blog = new Blog();
blog.setTitle("设计模式"+i);
blog.setAuthor("Bob"+i);
blog.setCreateTime(new Date());
blog.setViews(i*7);
list.add(blog);
}
System.out.println("插入是否成功:"+service.saveBatch(list));
System.out.println("总共有:"+service.count()+" 条数据");
}
}
第二章 M-P常用注解
@TableName
经过以上的测试,在使用MyBatis-Plus实现基本的CRUD时,我们并没有指定要操作的表,只是在 Mapper/dao接口继承BaseMapper时,设置了泛型Blog,而操作的表为blog表 由此得出结论,MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决 定,且默认操作的表名和实体类型的类名一致
如果表名和实体类不一致则会报错(首字母大小写不敏感,所以是怎么大小写没关系)
在实体类上添加@TableName("数据库表名")
,标识实体类对应的表,即可成功执行SQL语句
//设置实体类所对应的数据库表名
@TableName("blog")
public class Blog {
//....
}
Tips: 这里还有另一种方式让实体类找到表: 全局配置
这里的
tb_
是指的是数据库的前缀,他们的匹配规则是:前缀 + 定义的实体类类名 所以实体类类名还是要遵循数据表的取名规范去定义。
mybatis-plus:
global-config:
db-config:
table-prefix: tb_
@TableId
1)@TableId与他的两个属性
@TableId具有两个属性:value,type
@TableId
经过以上的测试,MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认 基于雪花算法的策略生成id。
若实体类和表中表示主键的不是id,而是其他字段,例如uid,MyBatis-Plus会自动识别uid为主 键列吗? 我们实体类中的属性id改为uid,将表中的字段id也改为uid,测试添加功能
value:
标注数据库表主键名
注意: @TableId注解只是将该字段指定为主键,如果数据库字段和属性名不一致,仍然会报错。但是可以通过 @TableId
注解的Value
属性指定数据库表名。
用法:@TableId(value="数据库主键字段名")
type:
设置主键以何种方式自增,用法:@TableId(type = IdType.AUTO)
常用的主键策略:(还有其他策略,值列举两种)
值 | 描述 |
---|---|
IdType.ASSIGN_ID(默 认) | 基于雪花算法的策略生成数据id,与数据库id是否设置自增无关 |
IdType.AUTO | 使用数据库的自增策略,注意,该类型请确保数据库设置了id自增, 否则无效 |
public class Blog {
//将属性所对应的字段指定为主键
@TableId(value="id",type = IdType.AUTO)
private Long id;
....
}
全局配置策略:
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
# 配置MyBatis-Plus的主键策略
id-type: auto
@TableField
- 若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格 例如实体类属性userName,表中字段user_name 此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格 相当于在MyBatis中配置
- 若实体类中的属性和表中的字段不满足情况1 例如实体类属性name,表中字段username 此时需要在实体类属性上使用@TableField("username")设置属性所对应的字段名
@TableField("create_time")
private Date createTime;
@TableLogic
逻辑删除 :
- 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
- 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库 中仍旧能看到此条数据记录 使用场景:可以进行数据恢复
实现逻辑删除:
第一步 数据库中创建逻辑删除状态列,设置默认值为0
第二步 实体类中添加逻辑删除属性
@TableLogic
private Integer isDelete;
第三步 测试
测试删除功能,真正执行的是修改 UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0 测试查询功能,被逻辑删除的数据默认不会被查询 SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
第三章 条件构造器和常用接口⭐
1)条件构造器简介
- Wrapper : 条件构造抽象类,最顶端父类
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper : 查询条件封装
- UpdateWrapper : Update 条件封装
- AbstractLambdaWrapper : 使用Lambda 语法
- LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
2)QueryWrapper 使用
2.1)组装查询条件
/**
* 查找作者名字中带“o”,展现量在60-555之间和标题不为空的用户信息
* SELECT id,title,author,create_time,views,is_delete FROM blog WHERE is_delete=0
* AND (author LIKE ? AND views BETWEEN ? AND ? AND author IS NOT NULL)
*/
@Test
public void getQueryWrapper01(){
//条件构造器
QueryWrapper<Blog> queryWrapper = new QueryWrapper<>();
queryWrapper.like("author","o")
.between("views",60,555) //ge >= ; le <= between [x~X]之间
.isNotNull("author");
List<Blog> resultList = service.list(queryWrapper);
resultList.forEach(System.out::println);
}
部分参数说明:
- ge :>= 用法:.ge(“字段名”,num)
- le :<= 用法:.ge(“字段名”,num)
- between : [x~X]之间 用法:.between("字段名",x,X)
2.2)组装排序条件
/**
* 根据views降序排序,若views相同则按照id升序排序,且没有views和title为空的不参与排序
* SELECT id,title,author,create_time,views,is_delete FROM blog WHERE is_delete=0
* AND (views IS NOT NULL AND title <> ?) ORDER BY views DESC,id ASC
*/
@Test
public void getQueryWrapper02(){
QueryWrapper<Blog> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("views") //倒叙
.orderByAsc("id") //升序
.isNotNull("views") //不是null
.ne("title",""); //不等于空
List<Blog> resultList = service.list(queryWrapper);
resultList.forEach(System.out::println);
}
注意:isNotNull这里有个小坑
如果数据库表字段是空而不是null,那么使用isNotNull就会失效,需要使用.ne("字段名","")
解决
原因是:空值也就是在字段中存储NULL值,空字符串就是字段中存储空字符(’’)。
区别:
1)占用空间区别:
空字符串(’’)的长度是0,是不占用空间的;而的NULL的长度是NULL,其实它是占用空间的。NULL columns require additional space in the row to record whether their values are NULL.
NULL列需要行中的额外空间来记录它们的值是否为NULL。2)插入/查询方式区别:
NULL值查询使用is null/is not null查询,而空字符串(’’)可以使用=或者!=、<、>等算术运算符。
3)COUNT 和 IFNULL函数
使用 COUNT(字段) 统计会过滤掉 NULL 值,但是不会过滤掉空字符串。
总结:
空字符串不占空间,NULL值占空间。当字段不为NULL时,也可以插入空字符串。
当使用 IS NOT NULL 或者 IS NULL 时,只能查出字段中没有不为NULL的或者为 NULL 的,不能查出空字符串。
判断NULL 用IS NULL 或者 is not null,SQL 语句函数中可以使用IFNULL()函数来进行处理,判断空字符用 =’‘或者<>’'来进行处理。
在进行count()统计某列的记录数的时候,如果采用的NULL值,会被系统自动忽略掉,但是空字符串是会进行统计到其中的。
2.3)组装删除条件
/**
* 删除title为空的用户信息
* UPDATE blog SET is_delete=1 WHERE is_delete=0 AND (title = ? AND title IS NULL)
*/
@Test
void getQueryWrapper03(){
QueryWrapper<Blog> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("title",""); //等于“”
queryWrapper.isNull("title");
System.out.println(service.remove(queryWrapper));
}
2.4)使用QueryWrapper修改
先定义QueryWrapper查询条件,修改时需要传两个值:对象
和queryWrapper
/**
* UPDATE blog SET title=?, author=? WHERE is_delete=0 AND (views <= ? AND author LIKE ? OR title = ?)
* 将views小于666并且author包含2或title为空的用户信息修改
*/
@Test
void getUpdateWrapper01(){
QueryWrapper<Blog> queryWrapper = new QueryWrapper<>();
queryWrapper.le("views",666)
.like("author","2")
.or()
.eq("title","");
Blog blog = new Blog();
blog.setAuthor("张三");
blog.setTitle("java编程思想");
System.out.println(service.update(blog,queryWrapper));
}
3)Mybatis-plus 分页插件
3.1)分页插件
复习SQL Limit:
# limit 起始位置,返回的条数 SELECT * FROM qinglan_tb LIMIT 0,4
要检索查询返回的行的一部分,请使用
LIMIT
和OFFSET
子句。 以下说明了这些子句的语法:SELECT column_list FROM table1 ORDER BY column_list LIMIT row_count OFFSET offset; SQL
在这个语法中,
row_count
确定将返回的行数。OFFSET
子句在开始返回行之前跳过偏移行。OFFSET
子句是可选的。 如果同时使用LIMIT
和OFFSET
子句,OFFSET
会在LIMIT
约束行数之前先跳过偏移行
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
第一步 添加配置类
@MapperScan("com.yang.dao") //可以将主类中的注解移到此处
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
第二步 测试
@Test
void testPage(){
//设置分页参数
Page<AccountPojo> page = new Page<>(1,3);
service.page(page,null);
//获取分页数据
List<AccountPojo> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
}
3.2)xml自定义分页
第一步 BlogMapper中定义接口方法
这里依旧使用的是mybatis-plus的分页插件,所以需要将Page放置于第一个参数
/**
*通过views查询用户信息分页
* @param page mybatis-plus所提供的分页对象,必须位于第一个参数的位置
* @param views
* @return
* 这里依旧使用的是mybatis-plus的分页插件,所以需要将Page放置于第一个参数
*/
Page<Blog> selectPageVo(@Param("page") Page<Blog> page, @Param("views")Integer views);
第二步 BlogMapper.xml中编写SQL
<!--Page<Blog> selectPageVo(@Param("page") Page<Blog> page, @Param("views")Integer views);-->
<select id="selectPageVo" resultType="Blog">
select * from blog where views > #{views}
</select>
这里resultType使用了类型别名Blog,所以在配置文件中需要配置一下:
mybatis-plus: # 配置类型别名所对应的包 type-aliases-package: com.yang.domain
第三步 测试
@Test
public Object testSelectPageVo(){
Page<Blog> page = new Page<>(1, 3);
blogDao.selectPageVo(page,80);
System.out.println(page);
return page;
}
3.3)乐观锁
场景模拟 :
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太 高,可能会影响销量。又通知小王,你把商品价格降低30元。 此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王 也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就 完全被小王的覆盖了。 现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1 万多。
数据表创建(t_product):
CREATE TABLE t_product(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY(id)
)
INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
代码实现冲突:
@Test
void testProduct01(){
//小李查询的商品价格
Product productLi = productDao.selectById(1);
System.out.println("小李查询的商品价格:"+productLi.getPrice());
//小王查询的商品价格
Product productWang = productDao.selectById(1);
System.out.println("小王查询的商品价格:"+ productWang.getPrice());
//小李将商品价格+50
productLi.setPrice(productLi.getPrice()+50);
productDao.updateById(productLi);
//小王将商品价格-30
productWang.setPrice(productWang.getPrice()-30);
productDao.updateById(productWang);
//老板查询的商品价格
Product productBoss = productDao.selectById(1);
System.out.println("老板查询的商品价格:"+ productBoss.getPrice());
}
执行代码后,price为70
Mybatis-Plus实现乐观锁:
第一步 修改实体类
添加 @Version注解,表示该字段为版本号
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}
第二步 添加乐观锁插件配置
@MapperScan("com.yang.dao")
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
第三步 测试
和上面测试代码一样,但是数据输出price为150,Version变为1,小李的操作执行了,小王的没有执行
第四步 优化测试
@Test
void testProduct01(){
//小李查询的商品价格
Product productLi = productDao.selectById(1);
System.out.println("小李查询的商品价格:"+productLi.getPrice());
//小王查询的商品价格
Product productWang = productDao.selectById(1);
System.out.println("小王查询的商品价格:"+ productWang.getPrice());
//小李将商品价格+50
productLi.setPrice(productLi.getPrice()+50);
int result1 = productDao.updateById(productLi);
System.out.println("小李修改的结果:"+result1);
//小王将商品价格-30
productWang.setPrice(productWang.getPrice()-30);
int result2 = productDao.updateById(productWang);
System.out.println("小王修改的结果:"+result1);
//优化地方
if (result2 == 0){
//失败重试,重新获取version并更新
Product productWangNew = productDao.selectById(1);
productWangNew.setPrice(productWangNew.getPrice()-30);
productDao.updateById(productWangNew);
}
//老板查询的商品价格
Product productBoss = productDao.selectById(1);
System.out.println("老板查询的商品价格:"+ productBoss.getPrice());
}
//老板查询的商品价格:120
//version:2
3.4)通用枚举
表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用MyBatis-Plus的通用枚举 来实现
第一步 创建通用枚举类型
在com.yang.enums包下创建枚举类,注意属性要打上 @EnumValue
注解,并创建get方法和构造方法
@Getter
public enum SexEnum {
MALE(1,"男"),
FEMALE(0,"女");
@EnumValue
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
在实体类中添加枚举属性:
private SexEnum sex;
第二步 配置扫描通用枚举
注意:mybatisplus从 3.5.2版本 开始无需配置扫描通用枚举了
mybatis-plus:
# 配置扫描通用枚举
# mybatisplus从 3.5.2版本 开始无需配置扫描通用枚举了
type-enums-package: com.yang.enums
第三步 测试
@Test
public void insertBlog(){
Blog blog = new Blog();
blog.setTitle("操作系统");
blog.setAuthor("李四");
blog.setCreateTime(new Date());
blog.setViews(11);
blog.setSex(SexEnum.MALE);
System.out.println(SexEnum.FEMALE); //FEMALE
int result = blogDao.insert(blog);
System.out.println(result);
}
第四章 代码生成器
第一步 导入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
第二步 创建生成类
public class FastAutoGeneratorTest {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false", "root", "root")
.globalConfig(builder -> {
builder.author("atguigu") // 设置作者
// .enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://mybatis_plus"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.yang") // 设置父包名
.moduleName("mybatisplus") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));
// 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("t_product") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
生成的效果
第五章 多数据源
适用于多种场景:纯粹多库、 读写分离、 一主多从、 混合模式等 目前我们就来模拟一个纯粹多库的一个场景,其他场景类似 场景说明: 我们创建两个库,分别为:mybatis_plus(以前的库不动)与mybatis_plus_1(新建),将 mybatis_plus库的product表移动到mybatis_plus_1库,这样每个库一张表,通过一个测试用例 分别获取用户数据与商品数据,如果获取到说明多库模拟成功
第一步 创建数据库及表
创建数据库mybatis_plus_1和表product
CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus_1`;
CREATE TABLE product
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id)
);
# 添加一条数据
INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
# 删除mybatis_plus库product表(我这里是mybatis)
use mybatis_plus;
DROP TABLE IF EXISTS product;
第二步 引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
第三步 配置多数据源
说明:注释掉之前的数据库连接,添加新配置
注意:?后需根据上面所诉补全
spring:
# 配置数据源信息
datasource:
dynamic:
# 设置默认的数据源或者数据源组,默认值即为master
primary: master
# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/mybatis_plus?.....
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
slave_1:
url: jdbc:mysql://localhost:3306/mybatis_plus_1?....
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
第四步 创建service
注意:此处省略了实体类和mapper
用户Service
public interface UserService extends IService<User> {
}
@DS("master") //指定所操作的数据源
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
商品Service
public interface ProductService extends IService<Product> {
}
@DS("slave_1")
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}
第五步 测试
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
@Test
public void testDynamicDataSource(){
System.out.println(userService.getById(1L));
System.out.println(productService.getById(1L));
}
结果:
- 都能顺利获取对象,则测试成功
- 如果我们实现读写分离,将写操作方法加上主库数据源,读操作方法加上从库数据源,自动切 换,是不是就能实现读写分离?
第六章 MyBatisX插件
1)MyBatisX插件简介
MyBatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率
但是在真正开发过程中,MyBatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表 联查,我们就需要自己去编写代码和SQL语句,我们该如何快速的解决这个问题呢,这个时候可 以使用MyBatisX插件
MyBatisX一款基于 IDEA 的快速开发插件,为效率而生。
MyBatisX插件用法:https://baomidou.com/pages/ba5b24/
安装:
2)MyBatisX代码快速生成
第一步 创建一个正常的springboot+mybatis-plus项目
第二步 使用IDEA连接数据库
- 点击Database连接数据库
- 选择一个数据表,右键选择第一项
- MyBatisX的一些配置
- 完成
3)使用MyBatisX插件CRUD
案例:在mapper接口中,定义方法名listAll
(会有提示),然后按下Alt+Enter
键就会自动补齐语句和生成SQL在ProductMapper.xml中
@Repository
public interface ProductMapper extends BaseMapper<Product> {
List<Product> listAll();
int insertAll(Product product);
}
生成的SQL语句
测试:
@Test
void contextLoads() {
System.out.println(mapper.selectList(null));
Product product = new Product();
product.setName("iPhone13");
product.setPrice(5999);
product.setVersion(0);
mapper.insertAll(product);
System.out.println(mapper.selectList(null));
}
效果:
标签:System,笔记,public,blog,println,Plus,MyBatis,id,out From: https://www.cnblogs.com/fwdba/p/16780025.html