文章目录
知识点
lomback的使用
// 代替我们的set、get、无参、toString方法,注意不包含有参构造
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
增删改查
mapper直接继承BaseMapper,里面给我们封装好了各种常用方法。
记得指定泛型实体类。
package com.qcby.mybatis.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qcby.mybatis.model.Person;
import org.apache.ibatis.annotations.Mapper;
public interface PersonMapper extends BaseMapper<Person>{
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.baomidou.mybatisplus.core.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
public interface BaseMapper<T> {
int insert(T var1);
int deleteById(Serializable var1);
int deleteByMap(@Param("cm") Map<String, Object> var1);
int delete(@Param("ew") Wrapper<T> var1);
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> var1);
int updateById(@Param("et") T var1);
int update(@Param("et") T var1, @Param("ew") Wrapper<T> var2);
T selectById(Serializable var1);
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> var1);
List<T> selectByMap(@Param("cm") Map<String, Object> var1);
T selectOne(@Param("ew") Wrapper<T> var1);
Integer selectCount(@Param("ew") Wrapper<T> var1);
List<T> selectList(@Param("ew") Wrapper<T> var1);
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> var1);
List<Object> selectObjs(@Param("ew") Wrapper<T> var1);
IPage<T> selectPage(IPage<T> var1, @Param("ew") Wrapper<T> var2);
IPage<Map<String, Object>> selectMapsPage(IPage<T> var1, @Param("ew") Wrapper<T> var2);
}
mybaties配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
常用注解
转换规则
如果不符合规则,就需要注解来设置。
@TableName 指定表名
@TableId 指定主键名
@TableField 指定普通字段
一定要加TableField的场景:
- 布尔类型,名字is开头的一定要注意加TableField
- 属性名与数据库关键字冲突
- 成员遍历不是字段 @TableField(exist = false)
@TableLogic
private Integer isDeleted;
测试删除功能,真正执行的是修改
UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0
测试查询功能,被逻辑删除的数据默认不会被查询
SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
0表示未被删除
1表示被删除了
常用配置
一般都有默认值,很多不用设置。
id自增,需要在数据库也开启才能使用。
基本增删改查的使用
package com.qcby.springboot.mapper;
import com.qcby.springboot.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
//selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
userMapper.selectList(null).forEach(System.out::println);
}
@Test
public void insert() {
User user = new User();
user.setAge(18);
user.setName("小宇");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println("id = " + user.getId());
}
@Test
public void delById() {
// 根据id删除
int insert = userMapper.deleteById(1760628979133440001L);
System.out.println("影响行号="+insert);
}
@Test
public void delByMap() {
// 根据map中条件删除
Map<String, Object> map = new HashMap<>();
map.put("age", 18);
map.put("name", "小宇");
int result = userMapper.deleteByMap(map);
System.out.println("受影响行数:"+result);
}
@Test
public void delByIds() {
// 根据ids集合批量删除
List<Long> idList = Arrays.asList(6L, 7L);
int result = userMapper.deleteBatchIds(idList);
System.out.println("受影响行数:"+result);
}
@Test
public void update() {
User user = new User();
user.setName("小王");
user.setId(1L);
userMapper.updateById(user);
}
@Test
public void selectMp() {
// 根据对象查询
User user = userMapper.selectById(1L);
System.out.println(user);
// 根据ids集合批量查询
List<Long> idList = Arrays.asList(1L, 2L);
userMapper.selectBatchIds(idList).forEach(System.out::println);
// 根据条件查询
Map<String, Object> map = new HashMap<>();
map.put("age", 18);
map.put("name", "小王");
userMapper.selectByMap(map).forEach(System.out::println);
}
}
条件构造器
wrapper用于构造条件语言。
updateWrapper 一些特殊需要setsql拼接的。
@Test
void testUpdateWrapper() {
List<Long> ids = new ArrayList<>();
ids.add(1L);
ids.add(2L);
ids.add(3L);
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200")
.in("id", ids);
userMapper.update(null, wrapper);
}
package com.qcby.springboot.mapper;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.qcby.springboot.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.internal.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserWrapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void test01() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a")
.between("age", 20, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
@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);
}
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
.like("name", "a")
.gt("age", 20)
.or()
.isNotNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
@Test
public void test05() {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
.like("name", "a")
.gt("age", 20)
.and(i -> i.gt("age", 20).or().isNull("email"));
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
/**
* 指定字段查询
*/
@Test
public void test06() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值 为null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
/**
* 子查询
*/
@Test
public void test07() {
//查询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<User>()
.inSql("id", "select id from t_user where id <= 3");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
/**
* 修改的时候可以不用单独的场景user对象,直接在updatewrpper上设置
*/
@Test
public void test08() {
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.set("age", 18)
.set("email", "abc@qq.com")
.like("name", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
userMapper.update(null, wrapper);
}
/**
* 动态判断
*/
@Test
public void test09() {
//模拟用户传入的条件
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.like(StringUtils.isNotBlank(username), "name", "a")
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
/**
* 动态判断
*/
@Test
public void test10() {
//模拟用户传入的条件
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.like(StringUtils.isNotBlank(username), User::getName, "a")
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
@Test
public void test11() {
//组装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);
}
}
自定义sql
@Test
void UpdateSqlWrapper() {
List<Long> ids = new ArrayList<>();
ids.add(1L);
ids.add(2L);
ids.add(3L);
int amount = 200;
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("id", ids);
userMapper.updateBalanceByIds(wrapper, amount);
}
public interface UserMapper extends BaseMapper<User>{
void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper, @Param("amount") int amount);
}
<update id="updateBalanceByIds">
update user set balance = balance - #{amount} ${ew.customSqlSegment}
</update>
IService
基本用法
service接口继承IService<实体类>
实现类实现对应service并继承ServiceImpl<对应mapper,实体类>
iUserService:
public interface IUserService extends IService<User>{
}
UserServiceImpl:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
ctrl + p 查看泛型
alt + 回车 建立对应测试
IUserServiceTest:
@SpringBootTest
class IUserServiceTest {
@Autowired
private IUserService userService;
@Test
void add() {
User user = new User();
user.setUsername("小宇");
user.setPassword("123");
user.setPhone("18688990011");
user.setBalance(200);
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userService.save(user);
}
@Test
void ListByIds() {
List<Long> ids = new ArrayList<>();
ids.add(1L);
ids.add(2L);
ids.add(3L);
List<User> users = userService.listByIds(ids);
users.forEach(System.out::println);
}
}
插件
分页
添加插件
@Configuration
@MapperScan("com.atguigu.mybatisplus.mapper") //可以将主类中的注解移到此处
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
测试:
传入页号和每页大小,自动查询。
/**
* 测试分页
*/
@Test
public void fy() {
Page<User> page = new Page<User>(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());
}
自定义分页
比如查找年龄大于20,并分页。
自己写一个接口,并实现对应的sql。
public interface UserMapper extends BaseMapper<User>{
public List<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
}
<!--SQL片段,记录基础字段-->
<sql id="BaseColumns">id,name,age,email</sql>
<!--IPage<User> selectPageVo(Page<User> page, Integer age);-->
<select id="selectPageVo" resultType="com.qcby.springboot.pojo.User">
SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > #{age}
</select>
测试:
/**
* 自定义测试分页
*/
@Test
public void zdfy() {
Page<User> page = new Page<User>(1, 5);
Integer age = 20;
userMapper.selectPageVo(page, age);
//获取分页数据
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());
}
注意事项 :
- sql后面不要加;号,因为分页会自动在其后拼接limit。
- 我们只需要写查询条件,分页会自动帮我们实现。
乐观锁 与 悲观锁
乐观锁
与并发编程相关,即,每次更新都先判断读取的是否是最新数据。
先添加插件:
@Configuration
public class MybatisPlusConfig {
/**
* mp分页插件
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
加上@Version注解。相当于并发编程中的版本号。
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}
测试:
@SpringBootTest
public class lockTest {
@Autowired
private ProductMapper productMapper;
/**
* 演示应用场景
*/
@Test
public void productUpdate() {
// 读取100
Product productli = productMapper.selectById(1);
// 读取100
Product productwang = productMapper.selectById(1);
//3、小李将价格加了50元,存入了数据库
productli.setPrice(productli.getPrice() + 50);
int result1 = productMapper.updateById(productli);
System.out.println("小李修改结果:" + result1);
//4、小王将商品减了30元,存入了数据库,会发现更新失败,返回了0,因为版本号对不上
productwang.setPrice(productwang.getPrice() - 30);
int result2 = productMapper.updateById(productwang);
System.out.println("小王修改结果:" + result2);
//最后的结果
Product p3 = productMapper.selectById(1L); // 150
// 若更新失败,重新读取最新数据,再更新
if(result2 == 0) {
//失败重试,重新获取version并更新
productwang = productMapper.selectById(1L);
productwang.setPrice( productwang.getPrice() - 30);
result2 = productMapper.updateById( productwang);
}
System.out.println("小王修改结果:" + result2);
System.out.println("最后的结果:" + p3.getPrice()); // 120
}
}
悲观锁
相当于并发编程中上锁,其他人更新时,不允许操作被上锁的数据。
不再过多赘述,重点是乐观锁。
枚举
如:用1代表男,2代表女。
配置扫描枚举类型包:
- 创建枚举类型
- 添加@EnumValue注解标识。
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
@Getter
public enum SexEnum {
MALE(1, "男"),
FEMALE(2, "女");
@EnumValue // 这里
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
测试:
@Autowired
private UserMapper userMapper;
/**
* 测试枚举,设置性别
*/
@Test
public void testEnums() {
User user = new User();
user.setName("Enum");
user.setAge(20);
user.setSex(SexEnum.FEMALE); // 直接利用枚举
int insert = userMapper.insert(user);
}
代码生成器
导入依赖:
<!--逆向工程依赖-->
<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>
写一个代码生成器的工具类。
配置参数运行,即可生成我们用到的基本的各种代码。
这里我们生成user表的service,mapper,impl等代码。
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
public class FastAutoGeneratorTest {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false",
"root",
"root")
//全局配置
.globalConfig(builder -> {
builder.author("qcby") // 设置作者
//.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("E://mybatis_plus"); // 指定输出目录
})
//设置包的
.packageConfig(builder -> {
builder.parent("com.qcby") // 设置父包名
.moduleName("springboot") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "E://mybatis_plus"));
// 设置mapperXml生成路径
})
//策略的配置
.strategyConfig(builder -> {
builder.addInclude("user"); // 设置需要生成的表名
// .addTablePrefix("t_", "c_"); // 设置过滤表前缀 可以设置多个值
})
.templateEngine(new FreemarkerTemplateEngine())
// 使用Freemarker引擎模板,默认的是Velocity引擎模板
//.execute()执行的意思
.execute();
}
}
多数据源
就是配置多个数据库,一个项目会用到多个数据库。
导入依赖。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
当然需要
配置数据库:
spring:
#配置数据源信息
datasource:
#配置数据源的类型
#type: com.zaxxer.hikari.HikariDataSource
#配置连接数据库的各个信息
#驱动类的版本 8.0以上用这个包 5.0以上用com.mysql.jdbc.Driver这个包
#driver-class-name: com.mysql.cj.jdbc.Driver
#url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false
#username: root
#password: root
dynamic:
# 设置默认的数据源或者数据源组,默认值即为master
primary: master
# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
strict: true
datasource:
master:
url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf8&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
slave_1:
url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf8&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
- 在写接口是,要选择其对应的数据库。
- 利用@Ds注解
如User接口使用的master配置的数据库。
在Product接口使用的slave_1配置的数据库。
就可以正常使用了。