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是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。对于静态类型的查找来说,一般直接便利或者用二分查找(不会对区间进行插入和删除操作)
场景:
- 根据i姓名查询考试成绩
- 通讯录,即根据姓名查询联系方式
- 不重复集合,即需要先搜索关键字是否已经在集合中
以上可能在查找时进行一些插入和删除的操作,即动态查找,就需要用Map进行一系列操作
注:Map最重要的特性就是去重!当我们遇到删除重复数据,或者找每个数据重复的时候,都可以用Map来解决
2)Map含义
Map是一个接口类,该类没有集成自Collection,该类中存储的是<key,value>结构的键值对,并且key一定是唯一的,不能重复。在Java中,我们可以使用Map来处理各种不同的数据类型,包括字符串、整数、布尔值、对象等。
Map使用注意点:
- Map是一个接口,不能直接实例化对象,如果要实例化只能实例化其实现类TreeMap或者HashMap
- Map中存放键值对的key是唯一的,value是可以重复的
- Map中的key可以全部分离出来,存储到Set来进行访问(因为kay不能重复)
- Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)
- Map中键值对的key不能直接修改,value可以修改,如果要修改key,只能先将该key删除,然后再来进行重新插入
3)Map常用方法及含义
常用方法如下:
- V get(Object key):返回key对应的value值
- V getOrDefault(Object key, V defaultValue):返回key对应的value,key不存在,返回默认值
- V put(K key, V value):设置key对应的value
- V remove(Object key):删除key对应的映射关系
- Set keySet():返回所有key的不重复集合
- Collection valued():返回所有value的可重复集合
- Set<Map, Entry<K, V>> entrySet():返回所有的key-value映射关系
- boolean containsKey(Object key):判断是否包含key
- 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