Mybatis-plus笔记

1.SpringBoot项目创建

1.1pom.xml配置

<!-- mybatis-plus启动器 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>

<!-- lombok用于简化实体类开发 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

lombok插件安装,路径如下:

File-Settings-Plugins-搜索lombok,安装插件

1.2配置文件

有两种配置文件,都可以用,yml中的某些格式比properties高一些

1.2.1application.properties

注意:mysql是5.x.x版本的用【com.mysql.jdbc.Diver】,8.x.x版本的使用【com.mysql.cj.jdbc.Diver】

#数据源类型,使用SpringBoot默认的HikariDataSource数据源
spring.datasource.type = com.zaxxer.hikari.HikariDataSource
#mysql是5.x.x版本的用【com.mysql.jdbc.Diver】,8.x.x版本的使用【com.mysql.cj.jdbc.Diver】
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Diver

1.2.2application.yml

注意:mysql8.0版本的url要加上时区设置,【ServerTimezone=GMT%2B8】,否则会抛异常

spring:
	datasource:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        #mysql8.0版本的url要加上时区设置,【ServerTimezone=GMT%2B8】,否则会抛异常
        url: jdbc:mysql://localhost:3306/mybatis_plus?ServerTimezone=GMT%2B8&characterEncodeing=utf-8&userSSL=false
        username:
        password:

2.entity

2.1LomBok

Lombok用于简化实体类开发

2.1.1Lombok配置

pom.xml中

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2.1.2Lombok注解

在实体类名上方添加注解:domain/entity/…

编译后在target中可看到自动生成了对应的实体处理函数

直接@Data,即可对实体类进行处理【不生成有参构造】,用的最多的也是@Data

@Data
public class A{}
2.1.2.1@NoArgsConstructor

无参构造注解

2.1.2.2@AllArgsConstructor

有参构造注解

2.1.2.3@ Getter

get方法

2.1.2.4@Setter

set方法

2.1.2.5@EqualsAndHashCode

重写equals和hash方法

2.1.2.6@Data

以上多个注解比较冗余,可用@Data代替,只写一个就好

不生成有参构造,是set、get、无参构造、equalsAndHash方法的集成

2.2其他注解

2.2.1实体类对应数据库表:@TableName(“”)

实体类默认对应数据库中的名字,但如果名称不一致,则无法对应,这时就需要指定,指定的注解即为@TableName(“”),用来设置实体类所对应的表名,添加位置在类名上方。

@Data
//设置实体类所对应的表名
@TableName("a")
public class A{}

2.2.2设置全局实体类对应数据库表

如果数据库中的每个表都会加上一个前缀,其他部分都与类名一致,则不需要全部加上@TableName()注解,可用全局配置来实现对应。

配置位置在application.yml中,如下:

mybatis-plus:
	#设置mybatis-plus的全局配置
	global-config:
		db-config:
			#设置实体类所对应的表的统一前缀
			table-prefix:t_

2.2.3设置实体类属性对应的字段设置成数据库主键:@TableId(“”)

默认实体类中名称为“id”的是主键,但如果没有“id”属性,如何把其他名称的标识为逐渐,如“uId”,则需要使用@TableId(“id”)注解,添加位置为属性值上方

单独用@TableId:自动对应数据库中id字段

使用@TableId(value = ‘’ ‘’):可指定对应数据库中不是叫id的字段设置为主键,如数据库中的主键名称为“uid”

MyBatis-Plus默认的主键值是用“雪花算法”计算得出,如果要替换为“自增”,则需要另行设置,不仅要在数据库中选中“自增“,在实体类中的@TableId注解中也要添加type属性

【如果指定了主键 @TableId 后,默认情况下(没有指定主键生成策略),主键会使用雪花算法生成应用于分布式系统的主键解决办法】

//将属性所对应的字段指定为主键
//@TableId注解的value属性用于指定数据库中所对应的主键字段
//@TableId注解的type属性用于指定主键的生成策略,默认为“雪花算法”生成,下面设置成了“自增”
@TableId(value="uid" type = IdType.AUTO)
private Long uId;
type = IdType.AUTO:数据库ID自增,该类型请确保数据库设置了 ID自增 否则无效
type = IdType.INPUT :程序自己指定主键
type = IdType.ASSIGN_ID :由框架自动生成指定的主键,使用的是雪花算法
type = IdType.ASSIGN_UUID :由框架自动生成给定主键,使用算法 UUID 算法

2.2.4全局设置主键生成策略

在application.yml中可设置全局主键生成策略,替换默认的“雪花算法”

mybatis-plus:
	#设置mybatis-plus的全局配置
	global-config:
		db-config:
			#设置实体类所对应的表的统一前缀
			table-prefix: t_
			#设置统一的主键生成策略,生成方式为“自增”
			id-type: auto

2.2.5设置其他普通字段对应数据库表:@TableField(“”)

对于实体类中的其他普通属性,可以用@TableField(“”)注解来标识对应的数据库字段

MyBatis-Plus可以把数据库中的“_”自动转化成相应的驼峰,如数据库中的“user_name”可自动对应实体类中的“userName”,但如果不是对应的驼峰命名,那就需要使用@TableField(“”)来标识对应的数据库字段了。

//指定属性所对应的字段名
@TableField("user_name")
private String name;

2.2.6逻辑删除:@TableLogic()

物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据

逻辑删除: 假删除,将对应数据中代表后是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录

使用场景:可以进行数据恢复

使用方法:先在数据库中新增字段“is_deleted”,类型为“int”,默认值为“0”;再在实体类中新设一个属性“isDeleted”,在属性上方添加@TableLogic()注解,使用如下:

@TableLogic()
private Integer isDeleted;

执行以上操作后,使用BaseMapper内置的方法后的所有删除方法都会修改为update,将“is_deleted”字段的值修改为“1”,逻辑删除后,执行查询功能就只能查出“未删除状态”的数据

删除操作改为“修改”操作;查询操作自动查询“未删除状态”的数据

2.3雪花算法

雪花算法是由Twitter红布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。

通过雪花算法算出来的ID,即使在不同表里面也是不会重复的

相同表的有序性,后添加的ID一定比之前添加的要大,跟时间有关

核心思想:长度共64bit

优点:整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率极高。

在分布式大体量级的项目中使用雪花算法是非常好的ID生成方式。

3.Mapper

3.1mapper接口

3.1.1继承BaseMapper

BaseMapper:是由MybatisPlus提供的一个通用的mapper,包含了对单表的各种增删改查方法

@Repository
public interface AMapper extend BaseMapper<A>{
    //自定义mapper方法
}

3.1.2@Repository

@Repository:将mapper接口标记为持久层组件,这样在注入时就不会出现红线报错【当然报错也能跑】

爆红原因:IOC容器中只能存在类所对应的bean,不能存在接口所对应的bean,将AMapper动态生成的代理类交给IOC管理,在IDEA编译时会认为当前的AMapper是无法自动装配的,但是在运行阶段是不会出现这个问题的。

3.2启动类配置@MapperScan

在启动类AApplication.java中添加@MapperScan注解,括号里面填写mapper包的路径

//用于扫描mapper接口所在的包
@MapperScan("com.mybatisplus.mapper")
public class AApplication{}

3.3测试类@SpringBootTest

新建一个类,在类名上方添加@SpringBootTest注解,即可进行测试

@SpringBootTest
public class ATest{
	//先注入mapper
	@Autowired
	private AMapper aMapper;
	
	@Test
	public void aTest(){
		//通过条件构造器查询一个list集合,若没有条件,则可以设置null为参数
		List<A> aList = aMapper.selectList(null);
		//foreach()输出list中的每一条记录
		aList.foreach(System.out::println);
	}
}

3.4运行后sql语句显示配置

sql语句在MybatisPlus中已经被封装,如果我们要查看详细的处理过程,可以通过配置来实现

在application.yml中设置

mybatis-plus:
	configuration:
	log-impl: org.apache.ibatis.logging.stdOutImpl

4.BaseMapper单表操作

BaseMapper里面定义了常用的单表操作方法,多表联查需要自定义

4.1新增功能:【1个】inser(T entity)

.inser(T entity)方法:传入参数是“实体类对象”,返回值是"受影响的行数"

执行SQL:INSERT INTO a (name , age) VALUES (? , ?)

@Test
public void testInsert(){
	A a = new A();
    a.setName("张三");
    int result = aMapper.insert(a);
    System.out.println("result"+result);
}

4.2删除功能:【5个】

4.2.1根据ID删除

.deleteById(id)方法:传入参数直接”id“,返回值”受影响的行数“

执行SQL:DELETE FROM a WHERE id=?

含义:通过单个ID执行删除功能

@Test
public void testDeleteById(){
	//数字太长int范围,后面加L表示是Long类型数,即不会报错
    int result = aMapper.deleteById(782738641574517648L);
    System.out.println("result"+result);
}

4.2.2通过Map条件删除

.deleteByMap()方法:传入参数”删除条件“,返回值”受影响的行数“

执行SQL:DELETE FROM a WHERE name = ? AND ang = ?

含义:根据map集合中所设置的条件删除实体数据

@Test
public void testDeleteByMap(){
	//创建一个Map集合,键是String类型,值是Object类型,往Map里面存放一些条件
	Map<String,Object> map = new HashMap<>();
	//将删除条件以键值对的方法放置到map集合中,删除【name="张三",age=23】的实体,并关系,同时满足
	map.put("name","张三");
	map.put("age",23);
	//将map作为一个参数放在删除方法中
    int result = aMapper.deleteByMap(map);
    System.out.println("result"+result);
}

4.2.3批量删除

.deleteBatchIds(idList)方法:传入参数”id集合,多个id“,返回值”受影响的行数“

执行SQL:DELETE FROM a WHERE id IN (? , ? , ?)

含义:通过多个ID进行批量删除

@Test
public void testDeleteBatchIds(){
	//先创建一个集合,存放多个ID,Array.asList()方法可以直接把数据转换成一个集合
	List<Long> list = Array.asList(1L,2L,3L)
    int result = aMapper.deleteBatchIds(list);
    System.out.println("result"+result);
}

4.3修改功能:【2个】

4.3.1根据ID修改

.updateById(entity)方法:传入参数”实体类对象“,返回值”受影响的行数“

执行SQL:UPDATE a SET name = ? , age = ? WHERE id = ?

@Test
public void testUpdateById(){
	A a = new A();
	a.setId(4L);
	a.setName("李四");
	a.setAge(25);
    int result = aMapper.updateById(a);
    System.out.println("result"+result);
}

4.4查询功能:【很多】

4.4.1根据ID查询

.selectById(id)方法:传入参数”id“,返回值”实体对象“

执行SQL:SELECT id , name , age FROM a WHERE id = ?

@Test
public void testSelectById(){
	A a = aMapper.selectById(1L);
    System.out.println(a);
}

4.4.2批量查询

.selectBatchIds(idList)方法:传入参数”id集合对象“,返回值”List集合“

执行SQL:SELECT id , name , age FROM a WHERE id IN (? , ? , ?)

含义:根据多个ID查询多个实体信息

@Test
public void testSelectBatchIds(){
	List<Long> list = Array.asList(1L,2L,3L);
	List<A> aList = aMapper.selectBatchIds(list);
    aList.forEach(System.out.println);
}

4.4.3通过Map条件查询

.selectByMap(map)方法:传入参数”查询条件“,返回值”List集合“

执行SQL:SELECT id , name , age FROM a WHERE name = ? AND ang = ?

含义:根据map集合中的条件查询实体信息

@Test
public void testSelectByMap(){
	//创建一个Map集合,键是String类型,值是Object类型,往Map里面存放一些条件
	Map<String,Object> map = new HashMap<>();
	//将查询条件以键值对的方法放置到map集合中,查找【name="张三",age=23】的实体,并关系,同时满足
	map.put("name","Jack");
	map.put("age",20);
	//将map作为一个参数放在查询方法中
    List<A> aList = aMapper.selectByMap(map);
    aList.forEach(System.out.println);
}

插入:Map的使用

1)Map使用场景

Map是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。对于静态类型的查找来说,一般直接便利或者用二分查找(不会对区间进行插入和删除操作)

场景:

  1. 根据i姓名查询考试成绩
  2. 通讯录,即根据姓名查询联系方式
  3. 不重复集合,即需要先搜索关键字是否已经在集合中

以上可能在查找时进行一些插入和删除的操作,即动态查找,就需要用Map进行一系列操作

注:Map最重要的特性就是去重!当我们遇到删除重复数据,或者找每个数据重复的时候,都可以用Map来解决

2)Map含义

Map是一个接口类,该类没有集成自Collection,该类中存储的是<key,value>结构的键值对,并且key一定是唯一的,不能重复。在Java中,我们可以使用Map来处理各种不同的数据类型,包括字符串、整数、布尔值、对象等。

Map使用注意点:

  1. Map是一个接口,不能直接实例化对象,如果要实例化只能实例化其实现类TreeMap或者HashMap
  2. Map中存放键值对的key是唯一的,value是可以重复的
  3. Map中的key可以全部分离出来,存储到Set来进行访问(因为kay不能重复)
  4. Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)
  5. Map中键值对的key不能直接修改,value可以修改,如果要修改key,只能先将该key删除,然后再来进行重新插入
3)Map常用方法及含义

常用方法如下:

  1. V get(Object key):返回key对应的value值
  2. V getOrDefault(Object key, V defaultValue):返回key对应的value,key不存在,返回默认值
  3. V put(K key, V value):设置key对应的value
  4. V remove(Object key):删除key对应的映射关系
  5. Set keySet():返回所有key的不重复集合
  6. Collection valued():返回所有value的可重复集合
  7. Set<Map, Entry<K, V>> entrySet():返回所有的key-value映射关系
  8. boolean containsKey(Object key):判断是否包含key
  9. boolean containsValue(Object value):判断是否包含value

注意:Map是接口类,不能实例化对象,所以只能实例化其实现类TreeMap或者HashMap

HashMap和TreeMap区别:

  • HashMap是无序的,HashMap底层是由Hash桶实现的。由于哈希算法本身的优势,我们在进行增删改查的时候,HashMap的时间复杂度是O(1),是通过哈希函数计算的哈希地址。HashMap覆盖了equals()方法和hashcode()方法,使得HashMap中两个相等的映射返回相同的哈希值
  • TreeMap是有序的,默认根据key排序(升序),可以通过重写conparaTo()方法或使用比较器来更改排序规则。TreeMap底层是由树(红黑树)实现的,红黑树时间复杂度是O(log2 n)。TreeMap实现了SortedMap接口,使其有序
  • HashMap的工作效率更高,TreeMap则是基于树的增删改查,更推荐使用HashMap
4)Map基础操作使用
1.创建Map

在Java中,创建Map需要调用Map接口的实现类,最常用的实现类是HashMap

//创建一个HashMap对象,指定key的类型为String,value的类型为Integer,也可以替换成其他类型
Map<String, Integer> hashMap = new HashMap<>();
2.添加元素
//put(key, value)方法:向Map中添加元素
hashMap.put("Alice"26);
3.获取元素
//get(key)方法:根据键获取值
Integer AliceAge = hashMap.get("Alice");//26

//containsKey(key)方法:判断Map中是否存在指定的键
boolean hasAlice = hashMap.containsKey("Alice")//存在true

//containsValue(value)方法:判断Map中是否存在指定的值
boolean hasAge32 = hashMap.containsValue(32)//不存在false
4.遍历Map

遍历Map有两种方式,一种是遍历所有键值对,另一种是遍历所有键或者所有值

//遍历Map中的所有键值对
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
	String name = entry.getKey();
	Integer age = entry.getValue();
	sout(name + " 的年龄是 " + age);
}

//遍历Map中的所有键
for (String name : hashMap.keySet()) {
	sout."Name: " + name;
}

//遍历Map中的所有值
for (Integer age : hashMap.values()) {
	sout("Age: " + age);
}
5)Map高级操作使用
1.排序

Map本身是无序的,但是可以通过TreeMap类来实现排序

//使用TreeMap对HashMap进行排序,创建一个TreeMap对象,并将元素从HashMap中放入TreeMap中,由于TreeMap是按照键进行排序的,因此元素将会按照字典序排列
Map<String, Integer> treeMap = new TreeMap<>(hashMap);
2.合并

合并两个Map可以使用putAll()方法

//创建另一个HashMap
Map<String, Integer> anotherHashMap = new HashMap<>();
anotherHashMap.put("David", 29);
anotherHashMap.put("Ethan", 35);

//使用putAll()方法将anotherHashMap合并到原有的hashMap中
Map<String, Integer> mergedHashMap = new HashMap<>(hashMap);
mergedHashMap.putAll(anotherHashMap);
3.过滤

过滤Map中的元素,只需使用remove()方法删除不符合条件的元素即可

//过滤Map中的元素,使用迭代器遍历Map中的所有元素,如果值大于等于30,则将该键值对从Map中移除
Iterator<Map.Entry<String, Integer>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
	Map.Entry<String, Integer> entry = iterator.next();
	if(entry.getValue >= 30) {
		iterator.remove();
	}
}
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

//完整测试
public class TestMap {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("Alice",18);
        hashMap.put("Jack",20);
        hashMap.put("Dan",21);
        System.out.println("添加元素打印map集合:"+hashMap);

        Integer AliceAge = hashMap.get("Alice");
        System.out.println("根据key获取value:"+AliceAge);

        boolean hasAlice = hashMap.containsKey("Alice");
        System.out.println("判断集合中是否存在key:"+hasAlice);

        boolean hasAge = hashMap.containsValue(18);
        System.out.println("判断集合中是否存在value:"+hasAge);

        System.out.println("遍历Map中的所有键值对:");
        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            String name = entry.getKey();
            Integer age = entry.getValue();
            System.out.println(name + " 的年龄是 " + age);
        }

        System.out.println("遍历Map中的所有键");
        for (String name : hashMap.keySet()) {
            System.out.println("Name:"+name);
        }

        System.out.println("遍历Map中的所有值");
        for (Integer age : hashMap.values()) {
            System.out.println("Age:"+age);
        }

        System.out.println("\\n"+"排序操作:");
        Map<String, Integer> treeMap = new TreeMap<>(hashMap);
        System.out.println("创建treemap输出:"+treeMap);

        System.out.println("\\n"+"合并操作:");
        Map<String, Integer> anotherHashMap = new HashMap<>();
        anotherHashMap.put("Ethan",35);
        anotherHashMap.put("Tom",25);
        Map<String,Integer> mergedHashMap = new HashMap<>(hashMap);
        mergedHashMap.putAll(anotherHashMap);
        System.out.println("合并后的Map集合:"+mergedHashMap);

        System.out.println("\\n"+"过滤操作");
        Iterator<Map.Entry<String, Integer>> iterator = mergedHashMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            if (entry.getValue()>=30) {
                iterator.remove();
            }
        }
        System.out.println("根据条件过滤后的Map集合:"+mergedHashMap);
    }
}

4.4.5全部查询

.selectList()方法:传入参数”查询条件“,返回值”List集合“

执行SQL:SELECT id , name , age FROM a

含义:查询所有实体数据

@Test
public void aTest(){
    //通过条件构造器查询一个list集合,若没有条件,则可以设置null为参数
    List<A> aList = aMapper.selectList(null);
    //foreach()输出list中的每一条记录
    aList.foreach(System.out::println);
}

4.5自定义功能

MyBatis-Plus快速实现单表的增删改查,但对于多表联查等功能需要自定义

MyBatis-Plus只是在MyBatis作增强,而不做改变,原本MyBatis的功能依然可以使用

自定义方法可以创建xMapper.xml,按照MyBatis的方式去编写相应的功能

【找到resource目录】-【创建mapper文件夹】-【右键】-【new】-【mybatis-mapper】创建mapper模板

5.Service

Service层中Mybatis-Plus也提供了相应的继承类,但是真正的开发中不建议直接使用继承类中的方法,需要根据具体业务逻辑去自定义,仅作为扩展使用。

5.1接口:IService

public interface AService extends IService<A>{}

5.2实现类:ServiceImpl

5.2.1@Service

要在使用中访问到service实现类,则需要将其标志为一个组件,在ServiceImpl实现类上方添加@Service注解

@Service
public class AServiceImpl extends ServiceImpl<AMapper,A> implements AService{}

5.3测试类

使用service测试,进行数据操作

5.3.1查询总记录数

.count()方法:查询总记录数,"()"里面可以放置条件参数,返回值“记录条数”

@SpringBootTest
public class ATest{
	//直接注入Service
	@Autowired
	private AService aService;
	
	@Test
	public void testGetCount(){
		//查询总记录数
		long count = aService.count();
		System.out.println("总记录数:"+count);
	}
}

5.3.2批量添加

批量添加在Mapper里面无定义,因为实际需求量很大,不可能把成千上万的数据批量添加都放到同一条sql里面,所以此功能要放在Service里面来完成.

.saveBatch(list)方法:传入参数“list集合”,返回值“布尔值”【是否操作成功】

执行SQL:INSERT INTO a (id, name, age) VALUES (?, ?, ?)

含义:并未在sql中实现大批量插入,而是通过循环使用BaseMapper来执行单个insert语句,从而完成批量增加

@Test
public void testInsertMore(){
	List<A> aList = new ArrayList<>();
	for(int i=1;i<=10;i++){
		A a = new A();
		a.setName("ABC"+i);
		a.setAge(20+i);
		aList.add(a);
	}
	//返回值是布尔类型,看是否操作成功
    boolean b = aService.saveBatch(aList)
    System.out.println(b);
}

6.BaseMapper条件构造器使用

6.1Wapper介绍

Wapper:条件构造抽象类,最顶端父类

  • AbstractWapper:用于查询条件封装,生成sql的where条件
    • QueryWapper:查询条件封装
    • UpdateWapper:Update条件封装
    • AbstractLambdaWapper:使用Lambda语法
      • LambdaQueryWapper:用于Lambda语法使用的查询Wapper
      • LambdaUpdateWapper:Lambda更新封装Wapper

注意:删除功能里面用的跟查询是一样的,使用QueryWapper或LambdaQueryWapper

在这里插入图片描述

6.2QueryWapper使用

主要用于查询、删除功能中,修改功能也能用

6.2.1组装查询条件:.selectList(queryWapper)

注意:使用QueryWapper所有方法调用后的返回值都是QueryWapper,所以一个条件查询后可以使用链式结构调用**“.”**继续编写下一个条件,而不用分号直接分割开

6.2.1.1组装查询条件

使用QueryWapper完成以下需求:【查询用户名包含a,年龄在20~30,邮箱信息不为null的用户信息】

@Test
public void aTest(){
	//查询用户名包含a,年龄在20~30,邮箱信息不为null的用户信息
	//先创建一个QueryWapper
   QueryWapper<A> queryWapper = new QueryWapper<>();
   //包含“a”,使用QueryWapper中的模糊查询方法like("数据库字段名","模糊的值")
   //年龄在20~30,调用QueryWapper中的between("字段",value1,value2)
   //邮箱不为空,调用QueryWapper中的isNotNull("字段")
   queryWapepr.like("user_name","a")
   			  .between("age",20,30)
   			  .isNotNull("email");
   //将查询到的数据放到一个List中
   List<A> aList = aMapper.selectList(queryWapepr);
   aList.forEach(System.out.println);
}
6.2.1.2组装排序条件

使用QueryWapper完成以下需求:【查询用户信息,按照年龄的降序排序,如年龄相同,则按照id升序排序】

@Test
public void aTest(){
	//查询用户信息,按照年龄的降序排序,如年龄相同,则按照id升序排序
   QueryWapper<A> queryWapper = new QueryWapper<>();
   //年龄降序排使用.orderByDesc("字段名"),id升序排序使用.orderByAse("字段名")
   queryWapepr.orderByDesc("age")
   			  .orderByAse("id");
   List<A> aList = aMapper.selectList(queryWapepr);
   aList.forEach(System.out.println);
}
6.2.1.3模拟开发中组装条件的情况

真正的开发环境中,查询的条件值可能是由前端用户传入来决定的,所以每一个条件都要做校验,否则会影响我们最终查询的结果。

如下例:【查询前端传入的用户名(模糊查询)、年龄段(xx~xx)所对应的用户信息】

@Test
public void aTest(){
	String username = "";
	Integer ageBegin = 20;
	Integer ageEnd = 30;
	QueryWapper<A> queryQapper = new QueryWapper<>();
	if(StringUtils.isNotBlank(username)){
		//isNotBlank()判断某个字符串是否不为空字符串,不为null,不为空白符
		//如果不是空,则进行模糊查询
		queryWapper.like("user_name",username);
	}
	if(ageBegin != null){
		//.ge()方法表示大于等于
		queryWapper.ge("age",ageBegin);
	}
	if(ageEnd != null){
		//.le()方法表示小于等于
		queryWapper.le("age",ageEnd);
	}
	List<A> aList = aMapper.selectList(queryWapepr);
	aList.forEach(System.out.println);
}
6.2.1.4使用condition组装条件

更方便地实现上述功能:【查询前端传入的用户名(模糊查询)、年龄段(xx~xx)所对应的用户信息】

queryWapper中的.like()、.ge()、.le()还有另一个同名方法,增加了一个参数(boolean condition),包含condition判断,如果条件满足,则执行,不满足则不执行

直接将判断条件写在queryWapper方法的第一个参数位置,不用写再写冗长的if语句进行判断

@Test
public void aTest(){
	String username = "";
	Integer ageBegin = 20;
	Integer ageEnd = 30;
	QueryWapper<A> queryQapper = new QueryWapper<>();
	queryWapper.like(StringUtils.isNotBlank(username),"username",username)
			   .ge(ageBegin != null,"age",ageBegin)
			   .le(ageEnd != null,"age",ageEnd);
	List<A> aList = aMapper.selectList(queryWapepr);
	aList.forEach(System.out.println);
}

6.2.2组装删除条件:.delete(queryWapper)

使用QueryWapper完成以下需求:【删除邮箱地址为null的用户信息】

注意:添加了逻辑删除,所以此处操作是将数据对应字段从“未删除状态”修改为“已删除状态”

@Test
public void aTest(){
	//删除邮箱地址为null的用户信息
   QueryWapper<A> queryWapper = new QueryWapper<>();
   queryWapepr.isNull("email");
   int result = aMapper.delete(queryWapper);
   System.out.println("result"+result);
}

6.2.3组装修改条件:.update(entity, queryWapper)

第一个参数设置要修改的参数,第二个参数设置修改条件

使用QueryWapper完成以下需求:【将(年龄大于20并且用户名中含有a)或(邮箱为null)的用户信息修改】

@Test
public void aTest(){
	//修改(年龄大于20并且用户名中含有a)或(邮箱为null)的用户信息
   QueryWapper<A> queryWapper = new QueryWapper<>();
   //大于调用方法.gt("字段名",数值)
   //使用or()方法将上边条件与下方条件相联,表示“或”
   queryWapepr.gt("age",20)
   			  .like("user_name",a)
   			  .or()
   			  .isNull("email");
   //设置要修改的字段
   A a = new A();
   a.setName("小明");
   a.setEmail("123@qq.com");
   //传入设有修改参数的实体类a,查找修改条件进行修改
   int result = aMapper.update(a, queryWapper);
   System.out.println("result"+result);
}

如果条件优先级发生了变化,如何去完成?

解决方式:在.add()或.or()方法中使用lambda表达式,lambda中的条件将优先执行

修改需求:【将(用户名中含有a)并且(年龄大于20或邮箱为null)的用户信息修改】

.and()方法,表示“并”,里面参数是一个consumer,可以将条件构造器写在.and()里面,设置参数为"i",再写对“i”的操作【即为条件构造】

同样的.or()方法也有相同的用法。

@Test
public void aTest(){
   //修改(用户名中含有a)并且(年龄大于20或邮箱为null)的用户信息
   QueryWapper<A> queryWapper = new QueryWapper<>();
   //.and()方法,表示“并”,里面参数是一个consumer,可以将条件构造器写在.and()里面
   //设置.add()参数为"i",再写对“i”的操作【即为条件构造】
   //lambda中的条件优先执行
   queryWapepr.like("user_name",a)
   			  .and(i->
   			  		 i.gt("age",20)
   			  		 .or()
   			  		 .isNull("email"));
   //设置要修改的字段
   A a = new A();
   a.setName("小红");
   a.setEmail("123@qq.com");
   //传入设有修改参数的实体类a,查找修改条件进行修改
   int result = aMapper.update(a, queryWapper);
   System.out.println("result"+result);
}

6.2.4组装select字句:.selectMaps(queryWapper)

设置需要查询的字段,不将全部字段查询出来

需求:【查询用户的用户名、年龄、邮箱信息】

@Test
public void aTest(){
	//查询用户的用户名、年龄、邮箱信息
	QueryWapper<A> queryWapper = new QueryWapper<>();
	queryWapper.select("user_name","age","email");
	List<Map<String,Object>> maps = aMapper.selectMaps(queryWapper);
	maps.forEach(System.out.println);
}

6.2.5组装子查询

用子查询实现:【查询id小于等于100的用户信息】

使用.inSql()方法,第一个参数是子查询要判断的字段,第二个参数是子查询的sql【写判断数据的来源】

@Test
public void aTest(){
	//查询id小于等于100的用户信息
	QueryWapper<A> queryWapper = new QueryWapper<>();
	queryWapper.inSQL("id","select id from user where id <= 100");
	List<A> aList = aMapper.selectList(queryWapepr);
	aList.forEach(System.out.println);
}

6.3UpdateWapper使用

使用UpdateWapper实现修改功能:【将(年龄大于20并且用户名中含有a)或(邮箱为null)的用户信息修改】

使用updateWapper不用新创建实体类对象,直接用.set()方法实现修改参数的设置,在update()使用中因没有实体对象,所以第一个参数可设置为null,第二个参数放置updateWapper

@Test
public void aTest(){
    //修改(用户名中含有a)并且(年龄大于20或邮箱为null)的用户信息
	UpdateWapper<A> updateWapper = new UpdateWapper<>();
	updateWapper.like("user_name",a)
   			    .and(i->
   			  		 i.gt("age",20)
   			  		 .or()
   			  		 .isNull("email"));
   	//条件判断后直接用.set()方法设置修改值
   	updateWapper.set("user_name","小黑")
   			    .set("email","123@qq.com")
   	//使用updateWapper没有实体对象,所以第一个参数设为null
	int result = aMapper.update(null,updateWapper);
	System.out.println("result"+result);
}

6.4LambdaQueryWapper使用

使用QueryWapper、UpdateWapper可能会出现字段名写错的情况,提供了LambdaQueryWapper、LambdaUpdateWapper给我们解决,不用写数据库字段名,直接通过lambda表达式来访问实体类中所对应的属性即可,IDEA中编程可自动补全,不用像输入数据库字段一样需要自己确认无误。参数对比如下:

QueryWapper:.like(条件,“数据库对应字段”,输入匹配值)

LambdaQueryWapper:.like(条件,实体类::对应属性,输入匹配值)

需求同QueryWapper模拟实际开发的情况:【查询前端传入的用户名(模糊查询)、年龄段(xx~xx)所对应的用户信息】

@Test
public void aTest(){
	String username = "a";
	Integer ageBegin = null;
	Integer ageEnd = 30;
	//创建一个LambdaWapper对象
	LambdaQueryWapper<A> queryQapper = new LambdaQueryWapper<>();
	queryWapper.like(StringUtils.isNOtBlank(username)User::getName,username)
			   .ge(ageBegin != null,User:getAge,ageBegin)
			   .le(ageEnd != null,User:getAge,ageEnd);
	List<A> aList = aMapper.selectList(queryWapepr);
	aList.forEach(System.out.println);
}

QueryWrapper<A> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
    		.like(StringUtils.isNOtBlank(username)User::getName,username)
			.ge(ageBegin != null,User:getAge,ageBegin)
			.le(ageEnd != null,User:getAge,ageEnd);

6.5LambdaUpdateWapper使用

同LambdaQueryWapper用法。只要使用了lambda,则不能再在方法参数里面写字段名,需要用函数式接口来指定相应的字段。参数对比如下:

UpdateWapper:like(“数据库字段名”,输入匹配值)

LambdaUpdateWapper:.like(实体类::对应属性,输入匹配值)

需求同UpdateWapper:【将(年龄大于20并且用户名中含有a)或(邮箱为null)的用户信息修改】

@Test
public void aTest(){
	//修改(用户名中含有a)并且(年龄大于20或邮箱为null)的用户信息
	LambdaUpdateWapper<A> updateWapper = new LambdaUpdateWapper<>();
	updateWapper.like(User:getName,a)
   			    .and(i->
   			  		 i.gt(User:getAge,20)
   			  		 .or()
   			  		 .isNull(User:getEmail));
   	//条件判断后直接用.set()方法设置修改值
   	updateWapper.set(User:getName,"小黑")
   			    .set(User:getEmail,"123@qq.com")
   	//使用updateWapper没有实体对象,所以第一个参数设为null
	int result = aMapper.update(null,updateWapper);
	System.out.println("result"+result);
}

7.MyBatis-Plus提供的插件使用

7.1分页插件

MyBatis-Plus自带分页插件,只要简单的配置即可实现分页功能

7.1.1分页插件配置

7.1.1.1添加配置类:

项目下新建一个config包,存放MybatisPlusConfig类

7.1.1.2添加注解:

@Configuration:配置类,需要在类名上方添加@Configuration注解

@MapperScan(“mapper包路径”):启动类中扫描mapper接口的注解也可以挪到这里,放在启动类也可以,但是有了配置类后,建议添加到配置类中

@Bean:配置bean注解

分页功能需要用到的是一个拦截器,MyBatisPlusInterceptor

@Configuration
//扫描mapper接口所在的包
@MapperScan("mapper包路径")
punlic class MybatisPlusConfig{
	@Bean
	public MyBatisPlusInterceptor myBatisPlusInterceptor(){
		MyBatisPlusInterceptor interceptor = new MyBatisPlusInterceptor();
		//在插件中新增一个内部插件
		//设置数据库类型为MySQL
		interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MySQL));
		return interceptor;
	}
}

7.1.2测试分页功能

创建测试类:MyBatisPlusPluginsTest

.selectPage()方法:第一个参数“分页对象”,第二个参数“条件构造器”

MySQL中使用的分页关键字:limit,两个参数,第一个index【当前页的页码】,第二个pagesize【每页显示的条数】,page的参数也类似,第一个设置访问页码,第二个设置显示条数

@SpringBootTest
public class MyBatisPlusPluginsTest{

	@Autowired
	private AMapper aMapper;
	
	@Test
	public void aTest(){
		//访问第1页,设置每页显示3条数据
		Page<A> page = new Page<>(1,3);
		//第二个参数是条件构造器,可写null对所有数据进行分页查询
		aMapper.selectPage(page,null);
		//直接输出page对象查看结果
		System.out.println(page);
	}
}

当前页的起始索引:(当前页的页码-1)*每页显示的条数

7.1.3获取分页数据

page.getPages():获取总页数,返回数值,“有几页”

page.getRecodes():获取当前页数据,返回一个List集合

page.getTotal():获取总记录数,返回数值,“总共有几条”

page.hasNext():获取是否有下一页,返回值“true”或“false”

page.hasPrevious():获取是否有上一页,返回值“true”或“false”

page.getCurrent():获取当前页的页码,“第几页”

page.getSize():获取每页显示的条数,“每页显示几条”

@Test
public void aTest(){
    //访问第1页,设置每页显示3条数据
    Page<A> page = new Page<>(1,3);
    //第二个参数是条件构造器,可写null对所有数据进行分页查询
    aMapper.selectPage(page,null);
    //直接输出page对象查看结果
    System.out.println(page);
    //获取记录,返回值是一个List集合,获取当前页的数据
    System.out.println(page.getRecodes());
    //获取总页数
    System.out.println(page.getPages);
    //获取总记录数
    System.out.println(page.getTotal);
    //获取是否有下一页
    System.out.println(page.hasNext());
    //获取是否有上一页,当前第1页,即无上一页
    System.out.println(page.hasPrevious());
    //获取当前页的页码
    System.out.println(page.getCurrent);
    //获取每页显示的条数
    System.out.println(page.getSize());
}

7.1.5自定义分页功能

实际开发中,可能需求:当前查询语句是我们自定义的,在自己写的SQL语句中如何利用分页插件实现分页功能?

如需求:自定义mapper方法来根据年龄查询用户信息,并进行分页

AMapper.java:先在Mapper中创建方法

注意:page是MyBatis-Plus所提供的分页对象,必须位于第一个参数的位置

//通过年龄查询用户信息并分页
//若要分页插件作用于我们自己写的SQL,则传入的第一个参数一定是一个Page对象
//page,MyBatis-Plus所提供的分页对象,必须位于第一个参数的位置
//要根据年龄信息来查询并分页,所以年龄信息也要传进去
Page<A> selectPageVo(@Param("page") Page<A> page,@Param("age") Integer age);

AMapper.xml:将mapper方法放到映射文件中来实现

<!--实现mapper方法,其中分页功能用插件实现,自己写SQL的时候不用写分页功能-->
<!--返回值类型,被Page封装的实体类A-->
<select id="selectPageVo" resultType="A">
	<!--在自己的SQL中并没有实现分页功能,传输进来的page对象就是为了方便实现分页功能-->
	selcet id,user_name,age,email from t_user where age > #{age}
</select>

ATest.java中进行测试

@Test
public void aTest(){
	Page<A> page = new Page<>(1,3)
	//查询年龄大于20的用户信息
	//直接调用自定义的.selectPageVo(page,age)方法
	aMapper.selectPageVo(page,20);
	System.out.println(page.getRecodes());
}
7.1.5.1补充,类型别名设置

AMapper.xml文件中的resultType="A"的实体类A是一个类型别名,要通过类型别名来访问我们的类型的话,需要在配置文件application.yml中来设置

路径设置为实体类所在的包,则在此包之下的所有的类型我们都可以来使用它默认的别名,默认的别名就是类名,不区分大小写

mybatis-plus:
	#设置mybatis-plus的全局配置
	global-config:
		db-config:
			#设置实体类所对应的表的统一前缀
			table-prefix: t_
			#设置统一的主键生成策略,生成方式为“自增”
			id-type: auto
	#配置类别名所对应的包
	type-aliases-package:com.....pojo【实体类所在的包】

7.2乐观锁插件

情景:商品原价100,现需A操作将其+50,变为150;后又安排B在A操作结果上-30,变为120,但如果A、B同时操作,则可能出现脏数据,同时拿100,A+50,B-30,最终B将A操作覆盖,变为70,不满足预期,因此要通过加锁来解决冲突

解决方式:两种

乐观锁:B保存之前,检查价格是否被更改,如更改,则重新取出修改价格150,然后再存入120进数据库

悲观锁:A取出数据后,B只能等A操作完,才能对价格进行操作,也能保证最终价格是120

模拟冲突测试:

@Test
public void testProduct(){
	//小李查询商品价格
	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.uodateById(productLi);
	//小王将价格-50
	productWang.setPrice(productLi.getPrice+50);
	productMapper.uodateById(productWang);
	//老板查询商品价格
	Product productBoss = productMapper.selectById(1);
	System.out.println("老板查询的价格"+productBoss.getPrice)
}

以上测试执行结果是,小王将小李执行结果覆盖,老板查询到的价格是70

7.2.1乐观锁实现流程

乐观锁通过版本号来实现,设置一个version,更改后会更新版本,下一次更改则用新版本数据操作

数据库中添加version字段

取出记录时,获取当前version

更新时,将版本号+1,并将原来的版本号作为查询条件。

SELECT id,name,price,verison FROM t_product WHERE id = 1

更新时,version+1,如果where语句中的version版本不对,则更新失败

UPDATE product SET price = price+50, version = version+1 WHERE id = 1 AND version = 1

7.2.2Mybatis-Plus实现乐观锁

修改实体类:在版本号字段上方添加注解@version

@Data
public class Product{
	private Long id;
	private String name;
	private Integer price;
	@Version //标识乐观锁版本号字段
	private Integer version;
}

配置类中设置插件

@Configuration
//扫描mapper接口所在的包
@MapperScan("mapper包路径")
punlic class MybatisPlusConfig{
	@Bean
	public MyBatisPlusInterceptor myBatisPlusInterceptor(){
		MyBatisPlusInterceptor interceptor = new MyBatisPlusInterceptor();
		//在插件中新增一个内部插件
		//设置数据库类型为MySQL
		interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MySQL));
		//添加乐观锁插件
		interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
		return interceptor;
	}
}

同一段代码,测试结果变更,小王操作失败,老板查询到的数据是150

@Test
public void testProduct(){
	//小李查询商品价格
	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.uodateById(productLi);
	//小王将价格-50
	productWang.setPrice(productLi.getPrice+50);
	productMapper.uodateById(productWang);
	//老板查询商品价格
	Product productBoss = productMapper.selectById(1);
	System.out.println("老板查询的价格"+productBoss.getPrice)
}

以上还是未查询到120,下面进行优化修改

@Test
public void testProduct(){
	//小李查询商品价格
	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.uodateById(productLi);
	//小王将价格-30
	productWang.setPrice(productWang.getPrice-30);
	//新增一个操作结果的校验
	int result = productMapper.uodateById(productWang);
	if(result == 0){
		//操作失败,重试,重新设一个对象再进行操作
		product productWangNew = productMapper.selectById(1);
		productWang.setPrice(productWangNew.getPrice-30);
		productMapper.uodateById(productWangNew);
	}
	//老板查询商品价格
	Product productBoss = productMapper.selectById(1);
	System.out.println("老板查询的价格"+productBoss.getPrice)
}

以上,在小王操作时增加操作结果的校验,若操作失败,则重新取数据进行操作,即最终老板查询数据为120

8.通用枚举

数据库表中的某些字段值是固定的,例如性别(男/女),此时我们可以用MyBatis-Plus的通用枚举来实现

数据库设置性别:sex,类型int

创建一个枚举类

添加@EnumValue 注解将所标识的属性的值存储到数据库中,不然会报错,存入“男”与数据库类型int对不上,需要在sex上方添加注解,表示存入int类型的sex数据

//枚举类设置的是常量,只需要设置get方法,不用set,所以添加@Getter注解即可
@Getter
public enum SexEnum{
	//注意MALE后面是逗号","不是分号";"
	MALE(1,"男"),
	FEMALE(2,"女");
	
	@EnumValue //将注解所标识的属性的值存储到数据库中
	private Integer sex;
	private String sexName;
	
	//创建构造器
	SexEnum(Interger sex,String sexName){
		this.sex = sex;
	}
}

在实体类中,创建sex属性,要设置成枚举类型

private SexEnum sex;

设置了枚举类后,还需要在application.yml中添加扫描配置:

mybatis-plus:
	#设置mybatis-plus的全局配置
	global-config:
		db-config:
			#设置实体类所对应的表的统一前缀
			table-prefix: t_
			#设置统一的主键生成策略,生成方式为“自增”
			id-type: auto
	#配置类别名所对应的包
	type-aliases-package: com.....pojo【实体类所在的包】
	#扫描通用枚举的包
	type-enums-package: com.....enums【枚举类所在的包】

创建测试类测试下

@Test
public void test(){
	User user = new User();
	user.setAge(33);
	//设置性别为男
	user.setSex(SexEnum.Male);
	int result = userMapper.insert(user);
	System.out.println("result:"+result);
}

9.代码生成器

官网文档,引入依赖,执行官网上的代码,设置好数据库地址,生成文件地址即可,自动生成entity、mapper、mapper.xml、iService、ServiceImpl、controller

10.多数据源

适用于多种场景:纯粹多库、读写分离、一主多从、混合模式等

例如下纯粹多库场景:

创建两个库,分别为mybatis_plus和mybatis_plus_1(新建),将mybatis_plus库的product表移动到mybatis_plus_1中,这样每个库一张表,通过一个测试用例分别获取用户数据与商品数据,如果获取到说明多表模拟成功

10.1pom.xml引入依赖&&application.yml配置

引入多数据所需要的依赖,在pom.xml中设置

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version>
</dependency>

配置数据源,application.yml

spring:
	#配置数据源信息
	datasource:
        dynamic:
        	#设置默认的数据源或数据源组,默认值即为master
        	primary: master
        	#是否严格匹配数据源,默认false.使用false匹配不到会使用默认数据源,false使用默认数据源,设置成true的话在未匹配到指定数据源时会抛异常,这里设置成默认的false
        	strict: false
        	datasource:
        		master:
        			#设置master所对应的数据源信息
					url: jdbc:mysql://localhost:3306/mybatis_plus?ServerTimezone=GMT%2B8&characterEncodeing=utf-8&userSSL=false
					driver-class-name: com.mysql.cj.jdbc.Driver
                    username:root
                    password:123456
                slave_1:
                	#设置slave_1所对应的数据源信息
					url: jdbc:mysql://localhost:3306/mybatis_plus_1?ServerTimezone=GMT%2B8&characterEncodeing=utf-8&userSSL=false
					driver-class-name: com.mysql.cj.jdbc.Driver
                    username:root
                    password:123456
                    

10.2使用多数据源

在service层要指定对哪一个数据库进行操作

添加@DS(“master”)注解,用于指定要对哪一个数据源操作

创建用户Service

@DS("master")//指定所操作的数据源mybatis_plus
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{}

创建商品Service

@DS("slave_1")//指定所操作的数据源是mybatis_plus_1
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper,Product> implements ProductService{}

10.3测试多数据源

@SpringBootTest
public class Test{

	@Autowired
	private UserService userService;
	@Autowired
	private ProductService productService;
	
	@Test
	public void test(){
		System.out.println(userService.getById(1));
		System.out.println(productService.getById(1));
	}
}

以上测试可以对不同的数据库进行查询操作

如果我们要实现读写分离,将写操作方法加上主库数据源,读操作方法加上从库数据源,自动切换,则可实现读写分离

上面是在不同的Service类中指定不同的数据源,直接在类上添加@DS指定数据源即可,但是要在一个类里面使用多个数据源如何操作?

如果我们需要在同一个Service上操作,这时同样可以用@DS注解,不将@DS写在类上,直接写到类中具体的方法上,这样每个方法即可实现对指定数据源的操作。同时存在就近原则,【方法上注解】优于【类上注解】

11.MyBatisX插件

MyBatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率

但是在真正开发过程中,Mybatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表联查,我们就需要自己去编写代码和SQL语句,我们该如何快速的解决这个问题,这个时候可以使用MyBatisX插件

MyBatisX一款基于IDEA的快速开发插件,为效率而生。

MyBatisX插件用法:https://baomidou.com/pages/ba5b24/

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。

11.1MyBatisX定位mapper

安装MyBatisX插件后,可实现mapper接口与mapper.xml映射文件之间的相互跳转

首先创建好mapper和mapper.xml文件,在mapper.xml中填写好对应mapper路径后,即可点击方法跳转到对应的实现中

11.2MyBatisX代码快速生成

要使用代码自动生成的功能,需要在IDEA中的database这里将数据库连上才能使用

IDEA右侧Database–>左侧+号–>Data Source–>MySQL
在这里插入图片描述

填写数据库信息

在这里插入图片描述

11.3MyBatisX生成CRUD方法

一定要望文生义,select查询、insert添加、update修改、delete删除,然后MyBatisX可以自动提示补全,并生成实现SQL,使用十分方便。

11.3.1添加功能

在service接口中新建方法名选择MyBatisX提供的方法,如insertSelective,选中方法名,然后Alt+Enter,选择[MyBatisX] Generate MyBatis Sql

执行以上操作后,我们的方法在mapper和mapper.xml中就自动生成了,实现【有选择性的添加功能(如果字段为null,则不会出现在添加列表中)】

11.3.2删除功能

在mapper中创建方法:deleteByUidAndUserName(),MyBatisX会自动提示,选择后Alt+Enter,代码会自动补全,并生成mapper.xml实现SQL

11.3.3修改功能

修改用“update”,然后会自动提示,若要多个条件,加And,还会自动提示,By通过什么来修改

如:updateAgeAndUserNameByUid,Alt+Enter选择[MyBatisX] Generate MyBatis Sql,自动生成方法及对应SQL

11.3.4查询功能

如:根据年龄区间来查询年龄和性别:selectAgeAndSexByAgeBetween,使用MyBatisX自动生成

如:根据年龄来进行一个降序排序:selcetAllOrderByAgeDesc

动切换,则可实现读写分离

上面是在不同的Service类中指定不同的数据源,直接在类上添加@DS指定数据源即可,但是要在一个类里面使用多个数据源如何操作?

如果我们需要在同一个Service上操作,这时同样可以用@DS注解,不将@DS写在类上,直接写到类中具体的方法上,这样每个方法即可实现对指定数据源的操作。同时存在就近原则,【方法上注解】优于【类上注解】

11.MyBatisX插件

MyBatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率

但是在真正开发过程中,Mybatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表联查,我们就需要自己去编写代码和SQL语句,我们该如何快速的解决这个问题,这个时候可以使用MyBatisX插件

MyBatisX一款基于IDEA的快速开发插件,为效率而生。

MyBatisX插件用法:https://baomidou.com/pages/ba5b24/

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。

11.1MyBatisX定位mapper

安装MyBatisX插件后,可实现mapper接口与mapper.xml映射文件之间的相互跳转

首先创建好mapper和mapper.xml文件,在mapper.xml中填写好对应mapper路径后,即可点击方法跳转到对应的实现中

11.2MyBatisX代码快速生成

要使用代码自动生成的功能,需要在IDEA中的database这里将数据库连上才能使用

IDEA右侧Database–>左侧+号–>Data Source–>MySQL

[外链图片转存中…(img-t8QcXrZo-1719539356143)]

填写数据库信息

[外链图片转存中…(img-dg1F4tYU-1719539356143)]

11.3MyBatisX生成CRUD方法

一定要望文生义,select查询、insert添加、update修改、delete删除,然后MyBatisX可以自动提示补全,并生成实现SQL,使用十分方便。

11.3.1添加功能

在service接口中新建方法名选择MyBatisX提供的方法,如insertSelective,选中方法名,然后Alt+Enter,选择[MyBatisX] Generate MyBatis Sql

执行以上操作后,我们的方法在mapper和mapper.xml中就自动生成了,实现【有选择性的添加功能(如果字段为null,则不会出现在添加列表中)】

11.3.2删除功能

在mapper中创建方法:deleteByUidAndUserName(),MyBatisX会自动提示,选择后Alt+Enter,代码会自动补全,并生成mapper.xml实现SQL

11.3.3修改功能

修改用“update”,然后会自动提示,若要多个条件,加And,还会自动提示,By通过什么来修改

如:updateAgeAndUserNameByUid,Alt+Enter选择[MyBatisX] Generate MyBatis Sql,自动生成方法及对应SQL

11.3.4查询功能

如:根据年龄区间来查询年龄和性别:selectAgeAndSexByAgeBetween,使用MyBatisX自动生成

如:根据年龄来进行一个降序排序:selcetAllOrderByAgeDesc

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值