Mybatis-plus
零、简介
之前的mybatis的逆向工程帮助我们生成实体类、mapper接口、映射文件,而Mybatis-plus代码生成器可以为我们生成各个组件控制层、业务层、mapper接口、映射文件、实体类…
MybatisX是一个快速开发插件
- 组成部分
SQL都是动态生成的,操作的表由实体类决定,1.扫描实体类、2.分析表和实体类的关系(以及属性和字段的关系)、3.根据调用的方法生成对应的SQL语句并将增删改查语句注入到mybatis的容器中
一、快速启动
- 创建库、表,设置id为主键,设置id为bigInt,而不是int,因为mybatis插入的时候会默认使用雪花算法生成id,产生的id比较长,所以用bigint而不是int
CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 添加测试数据
- 创建springboot工程
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
loombok:
Lombok是一个Java库,能自动插入编辑器并构建工具,简化Java开发。通过添加注解的方式,不需要为类编写getter或eques方法,同时可以自动化日志变量。官网链接
简而言之:Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率。
- 编写实体类
使用lombak简化开发实体类
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class user {
private Long id;
private String name;
private Integer age;
private String email;
}
或者直接用
@Data
public class user {
private Long id;
private String name;
private Integer age;
private String email;
}
- 创建mapper接口
@Mapper
public interface UserMapper extends BaseMapper<User>{
}
继承的BaseMapper封装了很多方法,便于我们对单表进行增删改查
- 配置启动类
在启动类上配置mapper扫描注解用于扫描mapper包下的mapper文件
@SpringBootApplication
@MapperScan("com.wzhCode.mapper")
public class MybatisDemo01Application {
public static void main(String[] args) {
SpringApplication.run(MybatisDemo01Application.class, args);
}
}
配置测试类
@SpringBootTest
public class MybaisPlusTest {
@Autowired
private UserMapper userMapper;
//爆红原因,由于扫描的是mapper接口,而装配的实际上是动态生成的代理类,这时还没有代理类所以报错
//为了解决这个问题可以在mapper接口上标记一个@Repository,但是不写也不影响
@Test
public void testSelectList(){
List<User> users = userMapper.selectList(null);//内置参数为条件查询器,如果没条件则写null
users.forEach(System.out::println);
}
}
输出看不到mp执行的sql,解决方法,在yml中添加mp的配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
从中看出查询哪个表的哪些字段都是实体类的类名属性名决定的
二、关于Mapper
BaseMapper方法分析
package com.baomidou.mybatisplus.core.mapper;
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
* @param entity 实体对象
*/
int insert(T entity);
/**
* 根据 ID 删除
* @param id 主键ID
*/
int deleteById(Serializable id);
/**
* 根据实体(ID)删除
* @param entity 实体对象
* @since 3.4.4
*/
int deleteById(T entity);
/**
* 根据 columnMap 条件,删除记录
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
* @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where
语句)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 删除(根据ID 批量删除)
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 根据 ID 修改
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
/**
* 根据 whereEntity 条件,更新记录
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成
where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
/**
* 根据 ID 查询
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends
Serializable> idList);
/**
* 查询(根据 columnMap 条件)
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object>
columnMap);
/* 根据 entity 条件,查询一条记录
* <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常
</p>
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
List<T> ts = this.selectList(queryWrapper);
if (CollectionUtils.isNotEmpty(ts)) {
if (ts.size() != 1) {
throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
}
return ts.get(0);
}
return null;
}
/**
* 根据 Wrapper 条件,查询总记录数
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* <p>注意: 只返回第一个字段的值</p>
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page,@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
自定义方法
mybatis-plus只做增强,所以自定义方法和mybatis一样,需要写mapper和mapper对应的映射文件。所以需要配置映射文件的位置
mybatis-plus默认的文件位置在类路径下的mappewr下的任意目录的xml
如果想要自定义xml文件的位置,需要手动配置
三、关于service
通用IService
创建Service接口继承IService
public interface UserService extends IService<User> {
}
创建Service接口实现类并继承ServiceImpl<M,T>
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
四、sql和java代码映射问题
1.表名实体类名不对应
表和实体类的对应是通过实体类(继承BaseMapper接口时传入的泛型参数)决定的,当表名和实体类名不对应的时候就没办法使用了。
- 方法1:实体类上添加@TableName(“实际表名”)
- 方法2:全局配置
当所有表名和类名相比都有一个相同的前缀,可以设置全局配置来解决。
global-config:
db-config:
table-prefix: t_
2.主键问题-----@TableId
mp默认主键为id,当实际上主键不是id时会出现问题
在实体类对应的属性上标识注解**@TableId**
作用为将属性指定的字段作为主键
该注解的属性
- value:将主键对应属性和主键实际的字段相映射
如
id对应uid
//value属性用于指定主键的字段
@TableId("uid")
private Long id;
- type属性:设置主键生成策略
IdType.AUTO;自动递增
IdType.ASSIGN_ID;使用雪花算法(默认)
使用步骤,1.在sqlyog设置主键为自动增长2.设置@TableId的type属性为IdType.AUTO;
全局配置设置主键增长策略
global-config:
db-config:
id-type: auto
雪花算法
3.(非主键)和字段名的映射
下划线与驼峰
mp自动配置了下划线转驼峰
非主键属性与字段名映射关系----@TableField
eg:将实体类的name属性和表中的username字段映射
@TableField("username")
private String name;
4.逻辑删除功能
- 数据表中设置逻辑删除列
- 实体类添加逻辑属性并加上注解@TableLogic
@TableLogic
private Integer isDelete;
执行删除功能,实际上并不是去删除了,而是执行修改功能,讲逻辑列设置为1
- 配置yml
mybatis-plus:
global-config:
db-config:
logic-not-delete-value: 0
logic-delete-value: 1
再次查询,发现逻辑删除的数据无法查询到
五、条件构造器功能
组装查询条件
@Test
public void test01(){
//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username", "a")
.between("age", 20, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
组装排序条件
@Test
public void test02(){
//按年龄降序查询用户,如果年龄相同则按id升序排列
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
//is_deleted=0 ORDER BY age DESC,id ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.orderByDesc("age")
.orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
组装删除
删除使用的还是queryWrapper
@Test
public void test03(){
//删除email为空的用户
//DELETE FROM t_user WHERE (email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
//条件构造器也可以构建删除语句的条件
int result = userMapper.delete(queryWrapper);
System.out.println("受影响的行数:" + result);
}
修改功能实现
一种是使用queryWrapper----------userMapper.update(user, queryWrapper);
一种时使用updateWrapper----------userMapper.update(null, updateWrapper);
条件优先级
lambda表达式的条件优先执行
lambda表达式中的条件优先执行。
select子句
@Test
public void test05() {
//查询用户信息的username和age字段
//SELECT username,age FROM t_user
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("username", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值
为null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
实现子查询
@Test
public void test06() {
//查询id小于等于3的用户信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (id IN (select id from t_user where id <= 3))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from t_user where id <= 3");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
组装条件
在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则一定不能组装,以免影响SQL执行的结果。
- 思路一:
@Test
public void test08() {
//定义查询条件,有可能为null(用户未输入或未选择)
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
if(StringUtils.isNotBlank(username)){
queryWrapper.like("username","a");
}
if(ageBegin != null){
queryWrapper.ge("age", ageBegin);
}
if(ageEnd != null){
queryWrapper.le("age", ageEnd);
}
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
- 思路二
上面的实现方案没有问题,但是代码比较复杂,我们可以使用带condition参数的重载方法构建查询条件,简化代码的编写
@Test
public void test08UseCondition() {
//定义查询条件,有可能为null(用户未输入或未选择)
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
queryWrapper.like(StringUtils.isNotBlank(username), "username", "a")
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >= ? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
LambdaQueryWrapper
和QueryWrapper相比,String类型的字段名参数修改成为了SFunction类型(函数式接口)的参数
@Test
public void test09() {
//定义查询条件,有可能为null(用户未输入)
String username = "a";
Integer ageBegin = 10;
Integer ageEnd = 24;
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
//避免使用字符串表示字段,防止运行时错误
queryWrapper
.like(StringUtils.isNotBlank(username), User::getName, username)
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
LambdaUpdateWrapper
@Test
public void test10() {
//组装set子句
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper
.set(User::getAge, 18)
.set(User::getEmail, "user@atguigu.com")
.like(User::getName, "a")
.and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail)); //lambda表达式内的逻辑优先运算
User user = new User();
int result = userMapper.update(user, updateWrapper);
System.out.println("受影响的行数:" + result);
}
六、插件
1.分页插件
- 添加配置类
PaginationInnerInterceptor
@Configuration
@MapperScan("com.atguigu.mybatisplus.mapper") //扫描mapper接口所在的包
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
- 测试
@Test
public void testPage(){
//设置分页参数
Page<User> page = new Page<>(1, 5);
userMapper.selectPage(page, null);//第二个参数是条件构造器
//获取分页数据
List<User> 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());
}
2.xml自定义分页
- Mapper中自定义接口方法
注意返回值为Page类型,第一个参数为Page类型(分页对象,必须在第一个)
/**
* 根据年龄查询用户列表,分页显示
* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位
* @param age 年龄
* @return
*/
I
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
- xml中写SQL
注意去配置类型别名
<!--SQL片段,记录基础字段-->
<sql id="BaseColumns">id,username,age,email</sql>
<!--IPage<User> selectPageVo(Page<User> page, Integer age);-->
<select id="selectPageVo" resultType="User">
SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > #{age}
</select>
- 测试
@Test
public void testSelectPageVo(){
//设置分页参数
Page<User> page = new Page<>(1, 5);
userMapper.selectPageVo(page, 20);
//获取分页数据
List<User> 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.乐观锁插件
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就完全被小王的覆盖了。
现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1万多。
乐观锁与悲观锁
- 乐观锁:小王在保存价格前,会检查价格是否被人修改过。若改过,则取出修改后的价格——150减去30,这样将120存入
- 悲观锁:效力取出数据后,小王只能等待小李操作完之后才能操作,最终保证价格也是120元。
模拟冲突场景
MP的乐观锁插件的使用
- 实体类
给版本号对应属性加上注解@Version - 配置类
@Configuration
@MapperScan("com.wzhcode.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
注意这里的包扫描配置后,主类 的包扫描就得去掉了
- 测试
暂时不修改之前的代码
小张的修改操作没有被执行,因为他获得的版本号是0,而当前的版本号是1,所以没有执行。
最终只有小李操作成功。
优化测试代码:
@Test
public void testConcurrentVersionUpdate() {
//小李取数据
Good p1 = goodMapper.selectById(1L);
//小王取数据
Good p2 = goodMapper.selectById(1L);
//小李修改 + 50
p1.setPrice(p1.getPrice() + 50);
int result1 = goodMapper.updateById(p1);
System.out.println("小李修改的结果:" + result1);
//小王修改 - 30
p2.setPrice(p2.getPrice() - 30);
int result2 = goodMapper.updateById(p2);
System.out.println("小王修改的结果:" + result2);
if(result2 == 0){
//失败重试,重新获取version并更新
p2 = goodMapper.selectById(1L);
p2.setPrice(p2.getPrice() - 30);
result2 = goodMapper.updateById(p2);
}
4.通用枚举
问题场景
对于数据库中的sex字段想要使用枚举
- 写枚举类
@Getter
public enum SexEnum {
MALE(1,"男"),
FEMALE(2,"女");
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
由于枚举类里面都是常量,所以不需要写Setter只需要写Getter注解(lomback中的一个注解,用于简化实体类开发)
- 实体类
添加性别属性
private SexEnum sex;
- 测试
@Test
public void test(){
User user = new User();
user.setName("admin");
user.setSex(SexEnum.MALE);
int insert = userMapper.insert(user);
System.out.println("res:"+insert);
}
结果确报错
表中字段为int型,而枚举放的是String型的sexName属性而不是int型的sex属性
解决方法
- 在枚举类的对应属性上添加@EnumValue注解
@EnumValue
private Integer sex;
因为想要添加的是int型的数据
- 配置文件中扫描通用枚举
# 扫描通用枚举所在的包
type-enums-package: com.wzhcode.enums
插入成功
5.代码生成器
逆向工程通过表生成实体类和mapper和xml,而mybatis-plus的代码生成器生成的内容更多(控制层、业务层、持久层、mapper接口、xml映射…)
- 引入依赖
一个代码生成器的依赖,一个freemarker引擎模板依赖
<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 static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false", "root", "123456")
.globalConfig(builder -> {
builder.author("atguigu") // 设置作者//.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://mybatis_plus"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.atguigu") // 设置父包名
.moduleName("mybatisplus") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));// 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("t_user") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
七、多数据源
适用于多种场景:纯粹多库、读写分离、一主多从、混合模式等等
及那个两张表放在两个库里,看能不能处理多数据源的情况
引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
配置数据源
spring:
datasource:
dynamic:
#默认数据源或者数据源组
primary: master
#严格匹配数据源、默认false。true的时候如果匹配不到指定数据源会报异常,false使用默认数据源
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: '!Wzh2352186607'
slave_1:
url: jdbc:mysql://localhost:3306/mybatis_plus1?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: '!Wzh2352186607'
primary指的是默认数据源
strict为true时,匹配不到指定数据源会报异常,默认false匹配不到会找默认数据源
datasource下面是不同的数据源。
pojo
@Data
@TableName("t_user")
public class User {
@TableId(value = "id",type = IdType.AUTO)
private Long uid;
private String name;
private Integer age;
private String email;
@TableLogic
private Integer isDelete;
private Integer sex;
}
@Data
@TableName("t_goods")
public class Good {
@TableId
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}
mapper接口
public interface UserMapper extends BaseMapper<User> {
}
public interface GoodMapper extends BaseMapper<Good> {
}
主体类设置包扫描
@SpringBootApplication
@MapperScan("com.wzhCode.mapper")
public class MybatisPlusDemo2Application {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusDemo2Application.class, args);
}
}
Service层
public interface UserService extends IService<User> {
}
@Service
@DS("master")
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{
}
@Service用于将Service实现类交由ioc管理,@DS用于指定数据源
public interface GoodService extends IService<Good> {
}
@Service
@DS("slave_1")
public class GoodServiceImpl extends ServiceImpl<GoodMapper, Good> implements GoodService{
}
测试
@Test
public void test(){
System.out.println(userService.getById(1));
System.out.println(goodService.getById(1));
}
MybatisX插件
实际开发中mp无法为我们解决所有问题,例如复杂的SQL、多表联查。需要我们自己去开发代码和SQL语句,这个时候可以使用MybatisPlus插件。
它是一款基于IDEA的快速开发插件
MyBatisX插件用法:https://baomidou.com/pages/ba5b24/
- 安装
快速生成手脚架
需要连接到IDEA的database模块才能使用代码生成器的功能。
module path用于指定工程
base package用于指定包
relative package用于指定和数据相关联的实体类
en-Coding指定编码
ignore table fix:用于指定忽略的表名前缀
annotion:指定注解
option中的comment是注释,lomback、toString等是是否生成toString/使用lomback
生成自定义CRUD
只用写方法名,Mybatis-Plus会自动生成Sql