Mybatis-plus

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
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值