一.mybatis-plus技术学习
1.mybatis-plus简介和简单使用
2.mybatis-plus实战演练
3.mybatis-plus的重点类学习
4.mybatis-plus的注解学习
5.mybatis-plus的分页学习
6.mybatis-plus的乐观锁学习
7.mybatis-plus的查询条件
8.mybatis-plus细节
9.mybatis-plus的代码生成器具体使用
10.mybatis-plus自定义sql的注意点
一.mybatis-plus技术学习
1.mybatis-plu简介
- 对mybatis做了增强而未改变。
- 简单的使用
只需要提供scan entity
然后mapper接口只需要继承BaseMapper<>即可
然后service接口只需要继承IService<>即可
然后serviceImpl实现类只需要继承ServiceImpl<T, T> 实现 前面的Service接口即可。
最终可直接利用Service进行简单的增删改查操作
2.mybatis-plus实战演练
①总体流程
- 直接使用写好的sql
- 在数据库中创建表
- 在maven工程中引入依赖
- 创建实体类并让主程序mapperscan到
- 创建mapper继承baseMapper<实体类>
- 创建service继承IService<实体类>
- 创建serviceImpl继承ServiceImp<Mapper,实体类>实现service
- 直接使用即可
- 自定义sql
- 在mapper中自定义方法
- 在resouces中定义mapper.xml(绑定到mapper上)
- 直接使用即可。
- 对比mybatis
- 无需构建数据库连接池创建sqlsession连接(自动连接)
- 无需获取动态代理实现类(自动注入)
①创建工程,引入依赖,初始化数据库连接池,主启动
- 项目结构
- 主启动
package com.atguigu.mybatisplus;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.atguigu.mybatisplus.mapper")
@SpringBootApplication
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class,args);
}
}
- 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.likejin</groupId>
<artifactId>mybatis-plus</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.2.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
- 初始化数据库连接池
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:13306/mytest?useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=abc123
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
②建立数据库
CREATE TABLE USER
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
INSERT INTO USER (id, NAME, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
③建立对应实体类
package com.atguigu.mybatisplus.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("user")
public class User {
@TableId(value = "id")//主键列
private Long id;
@TableField(value = "name")//mp 会自动将数据库中的下划线风格转换为驼峰风格
private String name;
private Integer age;
private String email;
}
④建立mapper和mapper.xml
- 使用已有的sql
package com.atguigu.mybatisplus.mapper;
import com.atguigu.mybatisplus.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface UserMapper extends BaseMapper<User>{
List<User> selectAllByName(String name);
}
- 配置自定义sql查询
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.mybatisplus.mapper.UserMapper">
<sql id="Base_Column_List">
id, name, age, email
</sql>
<select id="selectAllByName" resultType="com.atguigu.mybatisplus.entity.User">
select
<include refid="Base_Column_List"/>
from user
where
name = #{name}
</select>
</mapper>
⑤初始化service和serviceImpl
- service
package com.atguigu.mybatisplus.service;
import com.atguigu.mybatisplus.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface UserService extends IService<User> {
List<User> listAllUserByName(String name);
}
- serviceImpl
package com.atguigu.mybatisplus.service.impl;
import com.atguigu.mybatisplus.entity.User;
import com.atguigu.mybatisplus.mapper.UserMapper;
import com.atguigu.mybatisplus.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
@Resource
UserMapper userMapper;
@Override
public List<User> listAllUserByName(String name) {
return userMapper.selectAllByName(name);
}
}
⑥测试
- 测试提供的sql语句
package com.atguigu.mybatisplus;
import com.atguigu.mybatisplus.entity.User;
import com.atguigu.mybatisplus.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MapperTest {
@Resource//按名称注入(autowired按类型注入)
UserMapper userMapper;
@Test
public void testSelcetList(){
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
- 测试自己写的sql语句
package com.atguigu.mybatisplus;
import com.atguigu.mybatisplus.entity.User;
import com.atguigu.mybatisplus.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyMapperTest {
@Resource//按名称注入(autowired按类型注入)
UserMapper userMapper;
@Test
public void testSelcetList(){
List<User> jone = userMapper.selectAllByName("Jone");
jone.forEach(System.out::println);
}
}
⑦测试结果
- 日志打印
3.mybatis-plus的重点类学习
- BaseMapper<>(mapper)
泛型接口,提供了较多的简单sql语句,只需提供泛型参数即可获得较多的简单sql方法(接口继承接口获得)
- IService<>(service)
泛型接口,提供对BaseMapper接口的封装service,只需提供泛型参数即可获得(接口继承接口)
- ServiceImpl<Mapper,实体类>(serviceImpl)
泛型类,实现了Iservice里的抽象方法,故实际使用只需要提供mapper和实体类泛型参数即可获得使用。实际使用要继承service
4.mybatis-plus的注解学习
①数据库表名@TableName(value =“t_user”)
- 解决问题:数据表名和JAVA中实体类名不对应(User,t_user)
- 具体使用:@TableName(value =“t_user”)
- 注意:自定义sql可以更改表名但是mybatis-plus自动生成sql语句(User对应user表)
②主键的使用@TabledId(value=“uid”)
- 解决问题:数据库主键名和JAVA实体类的字段名不对应。(mp自带默认雪花算法给出默认值id)
- 具体使用:@TabledId(value=“uid”)
- 注意:不使用mybatis-plus自己生成sql的insert语句时,如果插入并没有带入主键的值(为默认值null)且数据库没有主键策略,那么会报错(主键不能为空)。
- 注意:使用了mybatis-plus提供的sql的insert语句时,插入不带入主键的值,那么mybatis-plus会用雪花算法生成id(主键名必须为id,或者增加@TabledId注解)。
- 注意:即我们如果要用雪花算法。要不主键字段名为id,要不声明@TabledId(value=“uid”)
- 注意:如果要用自增算法,必须数据库中设置自增算法,java配置文件中全局自增设置(默认生成策略是雪花算法)
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
③雪花算法
- 出现场景
在数据库列多,查询量大的情况下,我们需要对数据库进行分表。 - 垂直分表和水平分表
垂直分表:常被查询的和不常被查询的分表即可。
水平分表:如果单表用户量很大,并发量很大,则需要水平分表。 - 主键策略
垂直分表:第二张表与第一张表主键相同即可。
水平分表:需要满足每张表自己的主键策略(自增id冲突)。需要满足负载均衡(一张表满了再添加下一张不均衡)。(主键id无法设置)引入雪花算法。 - 雪花算法
总共64位数据。
0位 符号位
41位 时间戳
10位 5位数据中心ID+5位机器ID
12位 序列号(毫秒级时间产生的序号)
④非主键字段名称不匹配@TabledField(value=“username”)
- 解决问题:非主键字段名称不匹配
- 具体使用:@TabledField(value=“username”)
- 注意:如果数据库为下划线,然后JAVA为驼峰,mybatis-plus自动转换
- 注意:如果没有映射规律,需要手动添加转换
⑤自动填充策略@TableField(fill = FieldFill.INSERT_UPDATE)
- 解决问题:每个表中的create_time和update_time两个字段都必须自动填充(可以在数据库设置,可以在java端设置)
- 具体使用:见下面代码
- 注意:阿里规范,每一个数据表必须有create_time和update_time两个字段
- 注意:可以在数据表结构中设置create_time和update_time
设置默认值为CURRENT_TIMESTAMP 并且update_time设置为根据时间更新,也可以在JAVA程序中设置自动填充功能。 - 自动填充逻辑
加入注解在插入时填充还是在更新时填充@TableField(fill = FieldFill.INSERT_UPDATE)
加入配置类并实现MetaObjectHandler提供的两个方法
根据字段,填充类型,填充数值去填充
(注意:所有填充内容都可写到该类,如果某一类没用到该填充字段,也会执行,故需要判断该类是否有该字段。如果某一类已经设置过值也会执行,故需要判断该对象是否已经赋值) - 实际运用:将全部类都需要填充的如创建时间和更新时间加入到该配置类中,其他不加(麻烦)
//插入时填充
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
//更新时填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
package com.atguigu.mybatisplus.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//注意此方法只要在填充时就回调用,效率不高,因为多个表需要填充,但是每次可能执行填充一个,但是全部都执行
log.info("填充插入");
//实现填充业务逻辑
this.strictInsertFill(metaObject,"createTime",LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject,"updateTime",LocalDateTime.class, LocalDateTime.now());
//判断当前对象的自动填充是否包含属性
boolean author = metaObject.hasSetter("author");
if(author) {
log.info("插入其他表");
this.strictInsertFill(metaObject, "author", LocalDateTime.class, LocalDateTime.now());
}
//判断当前对象的自动填充是否已经赋值
Object age = this.getFieldValByName("age", metaObject);
if(age==null) {
this.strictInsertFill(metaObject, "age", Integer.class, 3);
}
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("填充修改");
this.strictInsertFill(metaObject,"updateTime",LocalDateTime.class, LocalDateTime.now());
}
}
⑥逻辑删除@TableLogic
- 解决问题:即需要我们在删除重要表时改为逻辑删除,而不是直接删除
- 使用:@TableLogic
- 数据库增加字段(阿里规范名称)
- 表中增加字段(阿里规范名称)
@TableLogic
@TableField(value = "is_deleted")
//字段为0表示没删(false),1表示删除了(true)
private Boolean deleted;
- 测试
userMapper.deleteById(2L);
5.mybatis-plus的分页学习
①分页的使用
- 配置完分页插件之后,在进行分页查询时,先查询所有记录,然后封装到Page对象
- 然后再利用Page对象中的页码和数据再进行分页查询即在sql语句后加入limit语句
- 最终得到的page对象中包含所有的分页的基本信息和本次分页查询的结构
②配置分页插件
package com.atguigu.mybatisplus.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@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;
}
}
③分页查询测试
package com.atguigu.mybatisplus;
import com.atguigu.mybatisplus.entity.User;
import com.atguigu.mybatisplus.mapper.UserMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class InterceptorTest {
@Resource
UserMapper userMapper;
@Test
public void testSelectPage(){
//分页插件填充了userPage的属性值
//然后执行sql语句也填充了userPage的分页查询
Page<User> userPage = new Page<>(2, 5);
userMapper.selectPage(userPage, null);
List<User> records = userPage.getRecords();
records.forEach(System.out::println);
}
}
④自定义方法分页查询
- 定义分页查询方法(mybatis-plus查询时检测到有page会自动增加limit)
IPage<User> selectPageByPage(Page<?> page, Integer age);
- 定义sql语句(xml)
<select id="selectPageByPage" resultType="com.atguigu.mybatisplus.entity.User">
SELECT <include refid="Base_Column_List"/> FROM user WHERE age > #{age}
</select>
- 测试(也会先查询所有,然后再进行分页查询,第一次结果封装到pageParam的分页信息中,然后再对第二次分页查询到的具体结果封装)
@Test
public void testSelectPageVo(){
Page<User> pageParam = new Page<>(1,5);
userMapper.selectPageByPage(pageParam, 18);
List<User> users = pageParam.getRecords();
users.forEach(System.out::println);
}
6.mybatis-plus的乐观锁学习
①乐观锁出现的场景
- 场景
数据库中一个商品卖100,老板让小李加50,后面一想太贵了,又让小王减去30。小李做事儿墨迹,于是小王小李同时看到商品此时只有100,小李100+50=150,小王100-30=70,最后小王后提交数据此时只有70元的商品。 - 增加product商品
CREATE TABLE product
(
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id)
);
INSERT INTO product (id, NAME, price) VALUES (1, '笔记本', 100);
- java中增加实体类和mapper
package com.atguigu.mybatisplus.entity;
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
package com.atguigu.mybatisplus.mapper;
import com.atguigu.mybatisplus.entity.Product;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface ProductMapper extends BaseMapper<Product> {
}
- 模拟场景
package com.atguigu.mybatisplus;
import com.atguigu.mybatisplus.entity.Product;
import com.atguigu.mybatisplus.mapper.ProductMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestConcurrentUpdate {
@Resource
ProductMapper productMapper;
@Test
public void test(){
//小李取数据
Product product = productMapper.selectById(1L);
//小王取数据
Product product1 = productMapper.selectById(1L);
//小李修改+50
product.setPrice(product.getPrice()+50);
productMapper.updateById(product);
//小王修改-30
product1.setPrice(product1.getPrice()-30);
productMapper.updateById(product1);
//老板看价格
Product product2 = productMapper.selectById(1L);
System.out.println("老板看价格"+product2);
}
}
②乐观锁的使用
- 增加版本机制
修改数据时看和自己拿到时的版本号是否相同,修改完成之后将版本号+1,所以小李修改完数据版本号变为1,但是小王发现自己拿的时候的版本号为0但是此时数据的版本号位1,所以重新拿版本号为1的数据修改,最终为120,版本号为2. - mybatis-plus的使用(本质修改时看version是否和数据库里的数据相同,如果相同则修改并且version+1,如果不相同则修改失败)
添加乐观锁的配置,对version字段加入@version注解
package com.atguigu.mybatisplus.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.atguigu.mybatisplus.mapper")
public class MybatisPlusConfig {
//注入分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
package com.atguigu.mybatisplus.entity;
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;
}
- 此时结果 小李修改成功,小王修改失败,老板看到150
小李查到的数据
小王查到的数据
小李修改数据
小王修改数据
老板查看数据
- 小王修改失败后再次修改
package com.atguigu.mybatisplus;
import com.atguigu.mybatisplus.entity.Product;
import com.atguigu.mybatisplus.mapper.ProductMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestConcurrentUpdate {
@Resource
ProductMapper productMapper;
@Test
public void test(){
//小李取数据
Product product = productMapper.selectById(1L);
//小王取数据
Product product1 = productMapper.selectById(1L);
//小李修改+50
product.setPrice(product.getPrice()+50);
productMapper.updateById(product);
//小王修改-30
product1.setPrice(product1.getPrice()-30);
int i = productMapper.updateById(product1);
if(i==0){
product1 = productMapper.selectById(1L);
product1.setPrice(product1.getPrice()-30);
productMapper.updateById(product1);
}
//老板看价格
Product product2 = productMapper.selectById(1L);
System.out.println("老板看价格"+product2);
}
}
7.mybatis-plus的查询条件
①三个重要的类
- 不用queryWrapper
可以用map封装查询条件,缺点是只能用and连接。
可以自定义sql,缺点是比较麻烦。 - QueryWrapper
用于封装where后的条件,或者order By - UpdateWrapper
可以封装where后的条件,或者order By
可以封装update …set 的条件 - LambdaQueryWrapper
可以直接用函数来得到实体类的字段对应于数据库中的字段的名称
②queryWrapper的使用
- where后面封装什么则写什么
封装 name like %n%,则写queryWrapper.like(“name”,“n”)
封装 age between 10 and 20 则写queryWrapper.between(“age”,10,20) - 封装order by id desc
queryWrapper.orderByDesc(“id”)
③测试
package com.atguigu.mybatisplus;
import com.atguigu.mybatisplus.entity.User;
import com.atguigu.mybatisplus.mapper.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest
public class WrapperTest {
@Resource
UserMapper userMapper;
@Test
public void testMapSelect(){
//map只能用and连接查询条件
//自定义sql语句
//SELECT id,name,age,email,create_time,update_time,is_deleted AS deleted FROM user
// WHERE name = ? AND age = ? AND is_deleted=0
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jone");
map.put("age",18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
//查询名字中包含n,年龄大于等于10且小于等于20,email不为空的用户
@Test
public void testWrapper1(){
//SELECT id,name,age,email,create_time,update_time,is_deleted AS deleted FROM user
// WHERE is_deleted=0
// AND (name LIKE ? AND age BETWEEN ? AND ?)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","n");//name Like %n%
queryWrapper.between("age",10,20);//age BETWEEN 10 AND 20
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
//按年龄降序查询用户,如果年龄相同则按id升序排列
@Test
public void testWrapper2(){
//SELECT id,name,age,email,create_time,update_time,is_deleted AS deleted FROM user
// WHERE is_deleted=0
// ORDER BY age DESC,id ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age");
queryWrapper.orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
//删除email为空的用户
@Test
public void testWrapper3(){
//UPDATE user SET is_deleted=1
// WHERE is_deleted=0 AND (email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int delete = userMapper.delete(queryWrapper);
}
//查询名字中包含n,且(年龄小于18或email为空的用户),并将这些用户的年龄设置为18,邮箱设置为 user@atguigu.com
@Test
public void testWrapper4(){
//UPDATE user SET age=?, email=?, update_time=?
// WHERE is_deleted=0 AND
// (name LIKE ? AND (age < ? OR email IS NULL))
//组装查询条件注意:不加连接就是and
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.like("name","n")
.and(i ->i.lt("age",18)
.or().isNull("email"));
//组装更新条件
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
//执行更新
userMapper.update(user,queryWrapper);
}
//查询所有用户的用户名和年龄
@Test
public void testWrapper5(){
//SELECT name,age FROM user
// WHERE is_deleted=0
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//组装select语句,通常会和selectMaps一起出现
queryWrapper.select("name","age");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
//查询id不大于3的所有用户的id列表
@Test
public void testWrapper6(){
//SELECT id,name,age,email,create_time,update_time,is_deleted AS deleted
// FROM user
// WHERE is_deleted=0 AND
// (id IN (select id from user where id <= 3))
//不推荐,不如直接写sql语句。
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id","select id from user where id <= 3");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
//查询名字中包含n,且(年龄小于18或email为空的用户),并将这些用户的年龄设置为18,邮箱设置为 user@atguigu.com
@Test
public void testWrapper7(){
//UPDATE user SET age=?,email=?
// WHERE is_deleted=0 AND
// (name LIKE ? AND (age < ? OR email IS NULL))
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper
.set("age",18)
.set("email","user@atguigu.com")
.like("name","n")
.and(i ->i.lt("age",18)
.or().isNull("email"));
//执行更新
//前面为null,没有自动填充功能,为user,有自动填充功能
userMapper.update(new User(),wrapper);
}
//查询名字中包含n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选的
@Test
public void testWrapper8(){
// SELECT id,name,age,email,create_time,update_time,is_deleted AS deleted FROM user
// WHERE is_deleted=0
// AND (name LIKE ? AND age >= ? AND age <= ?)
String name = "n";
Integer ageBegin = 10;
Integer ageEnd = 20;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//如果用户没有输入则不进行查询
if(StringUtils.isNotBlank(name)){
queryWrapper.like("name",name);
}
//简化开发,直接将判断条件写入
queryWrapper.ge(ageBegin != null,"age",ageBegin);
if(ageEnd != null){
queryWrapper.le("age",ageEnd);
}
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
//lambda就是可以直接利用User::getName可以直接获得在数据库表中的名称
@Test
public void testWrapper9(){
String name = "n";
Integer ageBegin = 10;
Integer ageEnd = 20;
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
if(StringUtils.isNotBlank(name)){
//User::getName可以获得user类的name属性在数据库user表中对应的字段名称(即前面写的@TableField)
queryWrapper.like(User::getName,name);
}
//简化开发
queryWrapper.ge(ageBegin != null,User::getAge,ageBegin);
if(ageEnd != null){
queryWrapper.le(User::getAge,ageEnd);
}
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
}
8.mybatis-plus细节
①resource和autowaired的区别
区别:resource是按优先名称注入,autowired是优先按类型注入。前者不是spring框架里的,后者是spring框架里的。
使用:在注入时,利用autowired注入会报红色,因为autowired是根据类型注入,而resource是根据名称注入,故用resource不会报红
②自定义xml文件时
可以在Resources中创建mapper包下的xml文件,默认扫描mapper包下的xml文件为sql。类似于mybatis中的Resource。(默认配置)
可以在Resources中创建对应的mapper接口的全类名的包,在mapperScan的时候就会扫描到并且绑定mapper接口和mapper.xml文件。类似于mybatis中的Package。(mapperscan)
③mapper.xml中注意可以生成字段
<sql id="Base_Column_List">
id, name, age, email
</sql>
<include refid="Base_Column_List"/>
④在serviceImpl中调用mapper时
不需要注入mapper,因为serviceImpl继承的ServiceImpl<Mapper,实体类>里面有结构为baseMapper(就是泛型参数的mapper),故也可以直接使用baseMapper.method
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
@Resource
UserMapper userMapper;
@Override
public List<User> listAllUserByName(String name) {
//等价于return userMapper.selectAllByName(name);
return baseMapper.selectAllByName(name);
}
}
⑤数据表为uid(主键),java实体类也为uid
插入时(不显式声明uid的值),会报错,即uid不会有值(不会用默认的雪花算法),mybatis-plus只会对字段名为id使用雪花算法然后带入默认值
解决:在uid字段上写@TabledId 就指明该uid为具体主键。
注意使用时@TabledId可以显式指明为自增策略,但是如果数据库不指明自增,则会报错。(数据库和业务都没有指明id,id是主键不为空,报错)
如果所有的字段的id都是自增,可以设置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
9.mybatis-plus的代码生成器具体使用
①代码生成器的作用
- 代码生成器能够根据数据表产生pojo的类,产生mapper,产生mapper.xml,生成service,产生controller,实际编写时只需要注重controller的逻辑即可。
②代码生成器的使用
- 引入依赖(父项目管理版本,看版本号在maven的父项目例子中)
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mybatis-plus 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<!-- Mybatis Plus 代码生成器模板引擎, -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
-
创建数据库中的表(注意规范,最好有注释,可以满足接口生成器Swagger的使用)
举例数据库表设计
-
在测试中创建代码生成器(@API是swagger接口文档生成器类注释)
package com.atguigu.srb.core;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
public class CodeGenerator {
@Test
public void genCode() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("Likejin");
//open等于true时,生成完代码后,打开资源管理器
gc.setOpen(false); //生成后是否打开资源管理器
//去掉service接口前缀I
gc.setServiceName("%sService"); //去掉Service接口的首字母I
//自增策略
gc.setIdType(IdType.AUTO); //主键策略
//自动生成接口文档@API
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:13306/db200921_srb_core?useUnicode=true&characterEncoding=utf8&useSSL=false");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("abc123");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.atguigu.srb.core");
pc.setEntity("pojo.entity"); //此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
mpg.setPackageInfo(pc);
// 5、策略配置
//能够完成数据库表名从下划线到类的驼峰习惯
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
//能够完成数据库字段名从下划线到类的属性驼峰习惯
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
//使用lombok的注解修饰
strategy.setEntityLombokModel(true); // lombok
//生成逻辑删除字段名
strategy.setLogicDeleteFieldName("is_deleted");//逻辑删除字段名
//符合阿里巴巴规范,去掉is前缀
strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀(确保tinyint(1))
//和controller的生成有关,全部使用@RestController
strategy.setRestControllerStyle(true); //restful api风格控制器,restController返回json数据
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
- 自动生成文档结构
10.mybatis-plus自定义sql的注意点
- 在mapper中定义方法
参数(Long userId,Long amount) - 在xml中注入参数用
#{userId}
#{amount} - 注意
如果在sql语句中不按顺序注入,则会报错绑定参数错误,需要在方法的参数中绑定@Param(“userId”)和@Param(“amount”)