LambdaXxxWrapper #
LambdaQueryWrapper主要是为了防止字段名写错
@Test public void test11(){ String username="abc"; Integer ageBegin=null; Integer ageEnd=30; LambdaQueryWrapper<User> queryWrapper=new LambdaQueryWrapper<>(); queryWrapper.like(StringUtils.isNotBlank(username),User::getUserName,username) .ge(ageBegin!=null,User::getAge,ageBegin); userMapper.selectList(queryWrapper); }
sql日志打印
==> Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (name LIKE ?) ==> Parameters: %abc%(String) <== Total: 0
LambdaUpdateWrapper
@Test public void test12() { //(age>23且用户名包含a) 或 (邮箱为null) LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.like(User::getUserName, "a") .and(userUpdateWrapper -> userUpdateWrapper.gt(User::getAge, 23).or().isNotNull(User::getEmail)); updateWrapper.set(User::getUserName, "小黑").set(User::getEmail, "abc@ly.com"); userMapper.update(null, updateWrapper); }
sql日志打印
==> Preparing: UPDATE t_user SET name=?,email=? WHERE is_deleted_ly=0 AND (name LIKE ? AND (age > ? OR email IS NOT NULL)) ==> Parameters: 小黑(String), abc@ly.com(String), %a%(String), 23(Integer) <== Updates: 0
MyBatis分页 #
先使用配置类
@Configuration @MapperScan("com.ly.mybatisplus.mapper") public class MyBatisConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return mybatisPlusInterceptor; } }
使用
@Test public void testPage() { Page<User> page = new Page<>(); page.setCurrent(2);//当前页页码 page.setSize(3);//每页条数 Page<User> userPage = userMapper.selectPage(page, null); System.out.println(userPage.getRecords() + "----\n" + userPage.getPages() + "----\n" + userPage.getTotal() + "---\n") ; }
sql日志打印
==> Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 LIMIT ?,? ==> Parameters: 3(Long), 3(Long) <== Columns: id, userName, age, email, is_deleted_ly <== Row: 4, 被修改了, 21, test4@baomidou.com, 0 <== Row: 5, 被修改了, 24, email被修改了, 0 <== Row: 6, 张三5, 18, test5@baomidou.com, 0 <== Total: 3
结果Page对象的数据
[User(id=4, userName=被修改了, age=21, email=test4@baomidou.com, isDeletedLy=0), User(id=5, userName=被修改了, age=24, email=email被修改了, isDeletedLy=0), User(id=6, userName=张三5, age=18, email=test5@baomidou.com, isDeletedLy=0)]---- 3---- 8---
自定义分页功能
首先,设置类型别名所在的包
mybatis-plus: type-aliases-package: com.ly.mybatisplus.pojo
在Mapper类中编写接口方法
@Repository public interface UserMapper extends BaseMapper<User> { /** * 通过年龄查询并分页 * @param page mybatis-plus提供的,必须存在且在第一个位置 * @param age * @return */ Page<User> selectPageVO(Page<User> page,Integer age); }
注意第一个参数
在Mapper.xml中编写语句
<select id="selectPageVO" resultType="User"> select uid,name,email from t_user where age > #{age} </select>
测试方法
@Test public void testPageCustom() { Page<User> page = new Page<>(); page.setCurrent(3);//当前页页码 page.setSize(5);//每页条数 Page<User> userPage = userMapper.selectPageVO(page, 12); System.out.println(userPage.getRecords() + "----\n" + userPage.getPages() + "----\n" + userPage.getTotal() + "---\n") ; }
sql日志输出
==> Preparing: SELECT COUNT(*) AS total FROM t_user WHERE age > ? ==> Parameters: 12(Integer) <== Columns: total <== Row: 20 <== Total: 1 //从第10行开始(不包括第10行),取5条记录 ==> Preparing: select uid,name,email from t_user where age > ? LIMIT ?,? ==> Parameters: 12(Integer), 10(Long), 5(Long) <== Columns: uid, name, email <== Row: 11, a, null <== Row: 12, a, null <== Row: 13, a, null <== Row: 14, a, null <== Row: 15, a, null <== Total: 5 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@706fe5c6] [null, null, null, null, null]---- 4---- 20---
注意上面那个sql,他会先查询条数,如果条数<=0,那么就不会执行下面的数据搜索了
悲观锁和乐观锁 #
场景
乐观锁根据版本号使用 version
乐观锁实现流程
模拟冲突 #
表创建
CREATE TABLE t_product ( id BIGINT ( 20 ) NOT NULL COMMENT '主键id', NAME VARCHAR ( 30 ) null DEFAULT NULL COMMENT '商品名称', price INT ( 11 ) DEFAULT 0 COMMENT '价格', version INT ( 11 ) DEFAULT 0 COMMENT '乐观锁版本号', PRIMARY KEY ( id ) )
创建ProductMapper
@Repository public interface ProductMapper extends BaseMapper<Product> { }
数据库数据
代码
@Test public void testModel() { //小李查询商品 Product productLi = productMapper.selectById(1L); //小王查询商品 Product productWang = productMapper.selectById(1L); //小李将商品加50 productLi.setPrice(productLi.getPrice()+50); productMapper.updateById(productLi); //小王将价格降低30 productWang.setPrice(productWang.getPrice()-30); productMapper.updateById(productWang); }
sql日志
==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? ==> Parameters: 外星人(String), 150(Integer), 0(Integer), 1(Long) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6325f352] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@70730db] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@91831175 wrapping com.mysql.cj.jdbc.ConnectionImpl@74ea46e2] will not be managed by Spring ==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? ==> Parameters: 外星人(String), 70(Integer), 0(Integer), 1(Long) <== Updates: 1
//最终结果为70
乐观锁插件 #
在实体类中使用@Version注解表示乐观锁版本号
@Version private Integer version;
配置类
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //添加乐观锁插件 mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; }
再次运行代码
@Test public void testModel() { //小李查询商品 Product productLi = productMapper.selectById(1L); //小王查询商品 Product productWang = productMapper.selectById(1L); //小李将商品加50 productLi.setPrice(productLi.getPrice()+50); productMapper.updateById(productLi); //小王将价格降低30 productWang.setPrice(productWang.getPrice()-30); productMapper.updateById(productWang); }
sql日志查看
==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? ==> Parameters: 外星人(String), 120(Integer), 1(Integer), 1(Long), 0(Integer) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2d64160c] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@33063f5b] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@356539350 wrapping com.mysql.cj.jdbc.ConnectionImpl@127a7272] will not be managed by Spring ==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? ==> Parameters: 外星人(String), 40(Integer), 1(Integer), 1(Long), 0(Integer) <== Updates: 0
优化修改流程 #
@Test public void testModel() { //小李查询商品 Product productLi = productMapper.selectById(1L); //小王查询商品 Product productWang = productMapper.selectById(1L); //小李将商品加50 productLi.setPrice(productLi.getPrice() + 50); productMapper.updateById(productLi); //小王将价格降低30 productWang.setPrice(productWang.getPrice() - 30); int i = productMapper.updateById(productWang); //如果小王操作失败,再获取一次 if (i == 0) { Product product = productMapper.selectById(1L); product.setPrice(product.getPrice() - 30); productMapper.updateById(product); } }
sql日志打印
==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? ==> Parameters: 外星人(String), 150(Integer), 6(Integer), 1(Long), 5(Integer) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@544e8149] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48a0c8aa] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@1637000661 wrapping com.mysql.cj.jdbc.ConnectionImpl@5f481b73] will not be managed by Spring ==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? ==> Parameters: 外星人(String), 70(Integer), 6(Integer), 1(Long), 5(Integer) <== Updates: 0 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48a0c8aa] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4cbc2e3b] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@43473566 wrapping com.mysql.cj.jdbc.ConnectionImpl@5f481b73] will not be managed by Spring ==> Preparing: SELECT id,name,price,version FROM t_product WHERE id=? ==> Parameters: 1(Long) <== Columns: id, name, price, version <== Row: 1, 外星人, 150, 6 <== Total: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4cbc2e3b] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@57562473] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@2050360660 wrapping com.mysql.cj.jdbc.ConnectionImpl@5f481b73] will not be managed by Spring ==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? ==> Parameters: 外星人(String), 120(Integer), 7(Integer), 1(Long), 6(Integer) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@57562473]
通用枚举 #
添加一个enum类
@Getter public enum SexEnum { MALE(1, "男"), FEMALE(2, "女"); private Integer sex; private String sexName; SexEnum(Integer sex, String sexName) { this.sex = sex; this.sexName = sexName; } }
数据库增加一个sex 字段,实体类增加一个sex属性
实体类
private SexEnum sex;
进行添加
@Test public void testEnum(){ User user=new User(); user.setUserName("enum - 测试名字"); user.setSexEnum(SexEnum.MALE); int insert = userMapper.insert(user); System.out.println(insert); }
注意看sql日志,有报错信息
==> Preparing: INSERT INTO t_user ( name, sex ) VALUES ( ?, ? ) ==> Parameters: enum - 测试名字(String), MALE(String) ### SQL: INSERT INTO t_user ( name, sex ) VALUES ( ?, ? ) ### Cause: java.sql.SQLException: Incorrect integer value: 'MALE' for column 'sex' at row 1
插入了非数字
修正,enum类添加注解
@EnumValue //将注解所标识的属性的值设置到数据库 private Integer sex;
扫描通用枚举的包 application.yml中
mybatis-plus: type-enums-package: com.ly.mybatisplus.enums
运行测试类并查看日志
==> Preparing: INSERT INTO t_user ( name, sex ) VALUES ( ?, ? ) ==> Parameters: enum - 测试名字(String), 1(Integer) <== Updates: 1
代码生成器 #
{% post_link study/mybatis_plus/official/hello 在28%进度的地方 %}
mybatis-plus 代码自动生成
maven 依赖
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity-engine-core --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency>
在测试类中编写程序让其自动生成
import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import org.apache.ibatis.jdbc.ScriptRunner; import java.io.InputStream; import java.io.InputStreamReader; import java.sql.Connection; import java.sql.SQLException; /** * <p> * 快速生成 * </p> * * @author lanjerry * @since 2021-09-16 */ public class FastAutoGeneratorTest { /** * 执行初始化数据库脚本 */ public static void before() throws SQLException { Connection conn = DATA_SOURCE_CONFIG.build().getConn(); InputStream inputStream = FastAutoGeneratorTest.class.getResourceAsStream("/db/schema-mysql.sql"); ScriptRunner scriptRunner = new ScriptRunner(conn); scriptRunner.setAutoCommit(true); scriptRunner.runScript(new InputStreamReader(inputStream)); conn.close(); } /** * 数据源配置 */ private static final DataSourceConfig.Builder DATA_SOURCE_CONFIG = new DataSourceConfig .Builder("jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&nullCatalogMeansCurrent=true", "root", "123456"); /** * 执行 run */ public static void main(String[] args) throws SQLException { before(); FastAutoGenerator.create(DATA_SOURCE_CONFIG) // 全局配置 .globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称"))) // 包配置 .packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名"))) // 策略配置 .strategyConfig((scanner, builder) -> builder.addInclude(scanner.apply("请输入表名,多个表名用,隔开"))) /* 模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker .templateEngine(new BeetlTemplateEngine()) .templateEngine(new FreemarkerTemplateEngine()) */ .execute(); } }
- shang gui gu 配置
- shang gui gu 配置
模拟多数据源环境 #
新建一个mybatis-plus数据库和表
maven依赖添加
<!-- https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.1</version> </dependency>
前提 使用mybatis_plus中的t_product表 及mybatis_plus1中的t_product1表
yml配置
spring: datasource: dynamic: primary: master #设置默认的数据源或者数据源组,默认值即为master strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 datasource: master: url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&&useSSL=false&&allowPublicKeyRetrieval=true username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置 slave_1: url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&&useSSL=false&&allowPublicKeyRetrieval=true username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver #slave_2: # url: ENC(xxxxx) # 内置加密,使用请查看详细文档 # username: ENC(xxxxx) # password: ENC(xxxxx) # driver-class-name: com.mysql.jdbc.Driver #......省略 #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
代码
- 结构
- 结构
安装MyBatisX插件 #
- 插件市场
- 自动定位
MyBatis代码快速生成 #
- 配置
- url及密码配置
- 使用
- 自动生成