基于互联网怎样做网站推广,wordpress 主题函数生成,网站盗取图片,中国的外贸企业有哪些Mybatis-Plus《学习笔记 22版尚硅谷 》 一、MyBatis-Plus1.简介2.特性3.支持数据库4.框架结构5.官方地址 二、入门案例1.开发环境2.建库建表3.创建工程4.配置编码5.测试查询 三、增删改查1.BaseMapperT2.调用Mapper层实现CRUD2.1 插入2.2 删除a、根据ID删除数据b、根据… Mybatis-Plus《学习笔记 22版尚硅谷 》 一、MyBatis-Plus1.简介2.特性3.支持数据库4.框架结构5.官方地址 二、入门案例1.开发环境2.建库建表3.创建工程4.配置编码5.测试查询 三、增删改查1.BaseMapperT2.调用Mapper层实现CRUD2.1 插入2.2 删除a、根据ID删除数据b、根据ID批量删除数据c、根据Map条件删除数据 2.3 修改2.4 查询a、根据ID查询用户信息b、根据多个ID查询多个用户信息c、根据Map条件查询用户信息d、查询所有用户信息 3.通用Service4.调用Service层操作数据 四、常用注解1.TableName1.1 引出问题1.2 解决问题a、使用注解解决问题b、使用全局配置解决问题 2.TableId2.1 引出问题2.2 解决问题2.3 TableId的value属性2.4 TableId的type属性 3.TbaleField3.1 情况一3.2 情况二 4.TableLogic4.1 逻辑删除4.2 实现逻辑删除 五、条件构造器1.Wrapper介绍2.QueryWrapper3.UpdateWrapper4.condition5.LambdaQueryWrapper6.LambdaUpdateWrapper 六、常用插件1.分页插件2.自定义分页3.乐观锁3.1 场景3.2 乐观锁与悲观锁3.3 模拟修改冲突3.4 乐观锁解决问题 七、通用枚举八、多数据源1.创建数据库及表2.新建工程引入依赖3.编写配置文件4.创建实体类5.创建Mapper及Service6.编写测试方法 九、MyBatisX插件1.安装MyBatisX插件2.快速生成代码3.快速生成CRUD 仅自用如有侵权立刻删——感谢【尚硅谷】官方文档
一、MyBatis-Plus
1.简介
MyBatis-Plus (opens new window)简称 MP是一个 MyBatis (opens new window)的增强工具在 MyBatis 的基础上只做增强不做改变为简化开发、提高效率而生。 我们的愿景是成为 MyBatis 最好的搭档就像 魂斗罗 中的 1P、2P基友搭配效率翻倍。 2.特性
无侵入只做增强不做改变引入它不会对现有工程产生影响如丝般顺滑损耗小启动即会自动注入基本 CURD性能基本无损耗直接面向对象操作强大的 CRUD 操作内置通用 Mapper、通用 Service仅仅通过少量配置即可实现单表大部分 CRUD 操作更有强大的条件构造器满足各类使用需求支持 Lambda 形式调用通过 Lambda 表达式方便的编写各类查询条件无需再担心字段写错支持主键自动生成支持多达 4 种主键策略内含分布式唯一 ID 生成器 - Sequence可自由配置完美解决主键问题支持 ActiveRecord 模式支持 ActiveRecord 形式调用实体类只需继承 Model 类即可进行强大的 CRUD 操作支持自定义全局通用操作支持全局通用方法注入 Write once, use anywhere 内置代码生成器采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码支持模板引擎更有超多自定义配置等您来使用内置分页插件基于 MyBatis 物理分页开发者无需关心具体操作配置好插件之后写分页等同于普通 List 查询分页插件支持多种数据库支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库内置性能分析插件可输出 SQL 语句以及其执行时间建议开发测试时启用该功能能快速揪出慢查询内置全局拦截插件提供全表 delete 、 update 操作智能分析阻断也可自定义拦截规则预防误操作
3.支持数据库 任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库具体支持情况如下如果不在下列表查看分页部分教程 PR 您的支持。 MySQLOracleDB2H2HSQLSQLitePostgreSQLSQLServerPhoenixGauss ClickHouseSybaseOceanBaseFirebirdCubridGoldilockscsiidb达梦数据库虚谷数据库人大金仓数据库南大通用(华库)数据库南大通用数据库神通数据库瀚高数据库
4.框架结构 5.官方地址 **官方网站**https://baomidou.com/ **官方文档**https://baomidou.com/pages/24112f/ 二、入门案例
1.开发环境
IDEIDEA 2019.3.5JDKJDK8构建工具Maven 3.5.4MySQLMySQL 8.0.24NavicatNavicat Premium 15Spring Boot2.6.7MyBatis-Plus3.5.1
2.建库建表 打开Navicat运行以下SQL脚本进行建库建表 CREATE DATABASE mybatis_plus /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use mybatis_plus;
CREATE TABLE user ( id bigint(20) NOT NULL COMMENT 主键ID, name varchar(30) DEFAULT NULL COMMENT 姓名, age int(11) DEFAULT NULL COMMENT 年龄, email varchar(50) DEFAULT NULL COMMENT 邮箱, PRIMARY KEY (id)
) ENGINEInnoDB DEFAULT CHARSETutf8;插入几条测试数据 INSERT INTO user (id, name, age, email) VALUES
(1, Jone, 18, test1baomidou.com),
(2, Jack, 20, test2baomidou.com),
(3, Tom, 28, test3baomidou.com),
(4, Sandy, 21, test4baomidou.com),
(5, Billie, 24, test5baomidou.com);3.创建工程 使用Spring Initializer快速初始化一个 Spring Boot 工程 引入MyBatis-Plus的依赖 dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.1/version
/dependency安装Lombok插件
4.配置编码 配置application.yml文件 #配置端口
server:port: 80spring:#配置数据源datasource:#配置数据源类型type: com.zaxxer.hikari.HikariDataSource#配置连接数据库的信息driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_plus?characterEncodingutf-8useSSLfalseusername: {username}password: {password}#MyBatis-Plus相关配置
mybatis-plus:configuration:#配置日志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl在 Spring Boot 启动类中添加 MapperScan 注解扫描 Mapper 文件夹 SpringBootApplication
MapperScan(指定Mapper接口所在的包)
public class MybatisPlusDemoApplication {public static void main(String[] args) {SpringApplication.run(MybatisPlusDemoApplication.class, args);}
}编写实体类 User.java此处使用了 Lombok 简化代码 Data
public class User {private Long id;private String name;private Integer age;private String email;
}编写 Mapper 包下的 UserMapper接口 public interface UserMapper extends BaseMapperUser {}5.测试查询 编写一个测试类MyBatisPlusTest.java SpringBootTest
public class MyBatisPlusTest {Resourceprivate UserMapper userMapper;/*** 测试查询所有数据*/Testvoid testSelectList(){//通过条件构造器查询一个list集合若没有条件则可以设置null为参数ListUser users userMapper.selectList(null);users.forEach(System.out::println);}
}控制台打印查询结果
三、增删改查
1.BaseMapper 说明: 通用 CRUD 封装BaseMapper 接口为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器泛型 T 为任意实体对象参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键对象 Wrapper 为条件构造器 MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现因此我们继承该接口以后可以直接使用。
本次演示的CRUD操作不包含参数带有条件构造器的方法关于条件构造器将单独在一个章节进行演示。 BaseMapper中提供的CRUD方法 增加Insert // 插入一条记录
int insert(T entity);删除Delete // 根据 entity 条件删除记录
int delete(Param(Constants.WRAPPER) WrapperT wrapper);
// 删除根据ID 批量删除
int deleteBatchIds(Param(Constants.COLLECTION) Collection? extends Serializable idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件删除记录
int deleteByMap(Param(Constants.COLUMN_MAP) MapString, Object columnMap);修改Update // 根据 whereWrapper 条件更新记录
int update(Param(Constants.ENTITY) T updateEntity, Param(Constants.WRAPPER) WrapperT whereWrapper);
// 根据 ID 修改
int updateById(Param(Constants.ENTITY) T entity);查询Selete // 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件查询一条记录
T selectOne(Param(Constants.WRAPPER) WrapperT queryWrapper);// 查询根据ID 批量查询
ListT selectBatchIds(Param(Constants.COLLECTION) Collection? extends Serializable idList);
// 根据 entity 条件查询全部记录
ListT selectList(Param(Constants.WRAPPER) WrapperT queryWrapper);
// 查询根据 columnMap 条件
ListT selectByMap(Param(Constants.COLUMN_MAP) MapString, Object columnMap);
// 根据 Wrapper 条件查询全部记录
ListMapString, Object selectMaps(Param(Constants.WRAPPER) WrapperT queryWrapper);
// 根据 Wrapper 条件查询全部记录。注意 只返回第一个字段的值
ListObject selectObjs(Param(Constants.WRAPPER) WrapperT queryWrapper);// 根据 entity 条件查询全部记录并翻页
IPageT selectPage(IPageT page, Param(Constants.WRAPPER) WrapperT queryWrapper);
// 根据 Wrapper 条件查询全部记录并翻页
IPageMapString, Object selectMapsPage(IPageT page, Param(Constants.WRAPPER) WrapperT queryWrapper);
// 根据 Wrapper 条件查询总记录数
Integer selectCount(Param(Constants.WRAPPER) WrapperT queryWrapper);2.调用Mapper层实现CRUD
2.1 插入 最终执行的结果所获取的id为1527206783590903810 这是因为MyBatis-Plus在实现插入数据时会默认基于雪花算法的策略生成id /*** 测试插入一条数据* MyBatis-Plus在实现插入数据时会默认基于雪花算法的策略生成id*/
Test
public void testInsert(){User user new User();user.setName(Vz);user.setAge(21);user.setEmail(vzoz6.cn);int result userMapper.insert(user);System.out.println(result 0 ? 添加成功 : 添加失败);System.out.println(受影响的行数为 result);//1527206783590903810当前 id 为雪花算法自动生成的idSystem.out.println(id自动获取 user.getId());
}2.2 删除 a、根据ID删除数据 调用方法int deleteById(Serializable id); /*** 测试根据id删除一条数据*/
Test
public void testDeleteById(){int result userMapper.deleteById(1527206783590903810L);System.out.println(result 0 ? 删除成功 : 删除失败);System.out.println(受影响的行数为 result);
}b、根据ID批量删除数据 调用方法int deleteBatchIds(Param(Constants.COLLECTION) Collection? extends Serializable idList); /*** 测试通过id批量删除数据*/
Test
public void testDeleteBatchIds(){ListLong ids Arrays.asList(6L,7L,8L);int result userMapper.deleteBatchIds(ids);System.out.println(result 0 ? 删除成功 : 删除失败);System.out.println(受影响的行数为 result);
}c、根据Map条件删除数据 调用方法int deleteByMap(Param(Constants.COLUMN_MAP) MapString, Object columnMap); /*** 测试根据Map集合中所设置的条件删除数据*/
Test
public void testDeleteByMap(){//当前演示为根据name和age删除数据//执行SQL为DELETE FROM user WHERE name ? AND age ?MapString,Object map new HashMap();map.put(name,Vz);map.put(age,21);int result userMapper.deleteByMap(map);System.out.println(result 0 ? 删除成功 : 删除失败);System.out.println(受影响的行数为 result);
}2.3 修改 调用方法int updateById(Param(Constants.ENTITY) T entity); /*** 测试根据id修改用户信息*/
Test
public void testUpdateById(){//执行SQL为 UPDATE user SET name?, age?, email? WHERE id?User user new User();user.setId(6L);user.setName(VzUpdate);user.setAge(18);user.setEmail(Vzsina.com);int result userMapper.updateById(user);System.out.println(result 0 ? 修改成功 : 修改失败);System.out.println(受影响的行数为 result);
}2.4 查询 a、根据ID查询用户信息 调用方法T selectById(Serializable id); /*** 测试根据id查询用户数据*/
Test
public void testSelectById(){User user userMapper.selectById(1L);System.out.println(user);
}b、根据多个ID查询多个用户信息 调用方法List selectBatchIds(Param(Constants.COLLECTION) Collection? extends Serializable idList); /*** 根据多个id查询用户数据*/
Test
public void testSelectBatchIds(){//执行SQL为SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )ListLong ids Arrays.asList(1L,2L,3L);ListUser users userMapper.selectBatchIds(ids);users.forEach(System.out::println);
}c、根据Map条件查询用户信息 调用方法List selectByMap(Param(Constants.COLUMN_MAP) MapString, Object columnMap); /*** 根据Map所设置的条件查询用户*/
Test
public void testSelectByMap(){//执行SQL为SELECT id,name,age,email FROM user WHERE age ?MapString,Object map new HashMap();map.put(age,18);ListUser users userMapper.selectByMap(map);users.forEach(System.out::println);
}d、查询所有用户信息 调用方法List selectList(Param(Constants.WRAPPER) Wrapper queryWrapper); /*** 测试查询所有数据*/
Test
void testSelectList(){ListUser users userMapper.selectList(null);users.forEach(System.out::println);
}3.通用Service 说明: 通用 Service CRUD 封装IService接口进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆泛型 T 为任意实体对象建议如果存在自定义通用 Service 方法的可能请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类对象 Wrapper 为 条件构造器 MyBatis-Plus中有一个接口 **IService**和其实现类 ServiceImpl封装了常见的业务层逻辑详情查看源码IService和ServiceImpl
因此我们在使用的时候仅需在自己定义的**Service接口中继承IService接口在自己的实现类中实现自己的Service并继承ServiceImpl**即可 IService中的CRUD方法 增加Save、SaveOrUpdate // 插入一条记录选择字段策略插入
boolean save(T entity);
// 插入批量
boolean saveBatch(CollectionT entityList);
// 插入批量
boolean saveBatch(CollectionT entityList, int batchSize);// TableId 注解存在更新记录否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, WrapperT updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(CollectionT entityList);
// 批量修改插入
boolean saveOrUpdateBatch(CollectionT entityList, int batchSize);删除Remove // 根据 entity 条件删除记录
boolean remove(WrapperT queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件删除记录
boolean removeByMap(MapString, Object columnMap);
// 删除根据ID 批量删除
boolean removeByIds(Collection? extends Serializable idList);修改Update // 根据 UpdateWrapper 条件更新记录 需要设置sqlset
boolean update(WrapperT updateWrapper);
// 根据 whereWrapper 条件更新记录
boolean update(T updateEntity, WrapperT whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(CollectionT entityList);
// 根据ID 批量更新
boolean updateBatchById(CollectionT entityList, int batchSize);查询Get、List、Count // 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper查询一条记录。结果集如果是多个会抛出异常随机取一条加上限制条件 wrapper.last(LIMIT 1)
T getOne(WrapperT queryWrapper);
// 根据 Wrapper查询一条记录
T getOne(WrapperT queryWrapper, boolean throwEx);
// 根据 Wrapper查询一条记录
MapString, Object getMap(WrapperT queryWrapper);
// 根据 Wrapper查询一条记录
V V getObj(WrapperT queryWrapper, Function? super Object, V mapper);// 查询所有
ListT list();
// 查询列表
ListT list(WrapperT queryWrapper);
// 查询根据ID 批量查询
CollectionT listByIds(Collection? extends Serializable idList);
// 查询根据 columnMap 条件
CollectionT listByMap(MapString, Object columnMap);
// 查询所有列表
ListMapString, Object listMaps();
// 查询列表
ListMapString, Object listMaps(WrapperT queryWrapper);
// 查询全部记录
ListObject listObjs();
// 查询全部记录
V ListV listObjs(Function? super Object, V mapper);
// 根据 Wrapper 条件查询全部记录
ListObject listObjs(WrapperT queryWrapper);
// 根据 Wrapper 条件查询全部记录
V ListV listObjs(WrapperT queryWrapper, Function? super Object, V mapper);// 查询总记录数
int count();
// 根据 Wrapper 条件查询总记录数
int count(WrapperT queryWrapper);分页Page // 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper查询一条记录。结果集如果是多个会抛出异常随机取一条加上限制条件 wrapper.last(LIMIT 1)
T getOne(WrapperT queryWrapper);
// 根据 Wrapper查询一条记录
T getOne(WrapperT queryWrapper, boolean throwEx);
// 根据 Wrapper查询一条记录
MapString, Object getMap(WrapperT queryWrapper);
// 根据 Wrapper查询一条记录
V V getObj(WrapperT queryWrapper, Function? super Object, V mapper);4.调用Service层操作数据 我们在自己的Service接口中通过继承MyBatis-Plus提供的IService接口不仅可以获得其提供的CRUD方法而且还可以使用自身定义的方法。 创建UserService并继承IService /*** UserService继承IService模板提供的基础功能 */
public interface UserService extends IServiceUser {}创建UserService的实现类并继承ServiceImpl /*** ServiceImpl实现了IService提供了IService中基础功能的实现 * 若ServiceImpl无法满足业务需求则可以使用自定的UserService定义方法并在实现类中实现*/
Service
public class UserServiceImpl extends ServiceImplUserMapper,User implements UserService{}测试查询记录数 调用方法int count(); Test
public void testGetCount(){//查询总记录数//执行的SQL为SELECT COUNT( * ) FROM userlong count userService.count();System.out.println(总记录数 count);
}测试批量插入数据 调用方法boolean saveBatch(Collection entityList); Test
public void test(){ListUser list new ArrayList();for (int i 1; i 10; i) {User user new User();user.setName(Vzi);user.setAge(20i);list.add(user);}boolean b userService.saveBatch(list);System.out.println(b ? 添加成功 : 添加失败);
}四、常用注解 MyBatis-Plus提供的注解可以帮我们解决一些数据库与实体之间相互映射的问题。 1.TableName 经过以上的测试在使用MyBatis-Plus实现基本的CRUD时我们并没有指定要操作的表只是在Mapper接口继承BaseMapper时设置了泛型User而操作的表为user表由此得出结论MyBatis-Plus在确定操作的表时由BaseMapper的泛型决定即实体类型决定且默认操作的表名和实体类型的类名一致。 1.1 引出问题 若实体类类型的类名和要操作的表的表名不一致会出现什么问题 我们将表user更名为t_user测试查询功能 程序抛出异常Table ‘mybatis_plus.user’ doesn’t exist因为现在的表名为t_user而默认操作的表名和实体类型的类名一致即user表
1.2 解决问题 a、使用注解解决问题 在实体类类型上添加TableName(t_user)标识实体类对应的表即可成功执行SQL语句 Data
TableName(t_user)
public class User {private Long id;private String name;private Integer age;private String email;
}b、使用全局配置解决问题 在开发的过程中我们经常遇到以上的问题即实体类所对应的表都有固定的前缀例如 t_ 或 tbl_ 此时可以使用MyBatis-Plus提供的全局配置为实体类所对应的表名设置默认的前缀那么就不需要在每个实体类上通过TableName标识实体类对应的表 mybatis-plus:global-config:db-config:# 设置实体类所对应的表的统一前缀table-prefix: t_2.TableId 经过以上的测试MyBatis-Plus在实现CRUD时会默认将id作为主键列并在插入数据时默认基于雪花算法的策略生成id 2.1 引出问题 若实体类和表中表示主键的不是id而是其他字段例如uidMyBatis-Plus会自动识别uid为主键列吗 我们实体类中的属性id改为uid将表中的字段id也改为uid测试添加功能 程序抛出异常Field ‘uid’ doesn’t have a default value说明MyBatis-Plus没有将uid作为主键赋值
2.2 解决问题 在实体类中uid属性上通过TableId将其标识为主键即可成功执行SQL语句 Date
public class User {TableIdprivate Long uid;private String name;private Integer age;private String email;
}2.3 TableId的value属性 若实体类中主键对应的属性为id而表中表示主键的字段为uid此时若只在属性id上添加注解TableId则抛出异常Unknown column ‘id’ in ‘field list’即MyBatis-Plus仍然会将id作为表的主键操作而表中表示主键的是字段uid此时需要通过TableId注解的value属性指定表中的主键字段TableId(uid)或TableId(valueuid) 2.4 TableId的type属性 type属性用来定义主键策略默认雪花算法 常用的主键策略
值描述IdType.ASSIGN_ID默认基于雪花算法的策略生成数据id与数据库id是否设置自增无关IdType.AUTO使用数据库的自增策略注意该类型请确保数据库设置了id自增
配置全局主键策略
#MyBatis-Plus相关配置
mybatis-plus:configuration:#配置日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:#配置mp的主键策略为自增id-type: auto# 设置实体类所对应的表的统一前缀table-prefix: t_3.TbaleField 经过以上的测试我们可以发现MyBatis-Plus在执行SQL语句时要保证实体类中的属性名和表中的字段名一致 如果实体类中的属性名和字段名不一致的情况会出现什么问题呢 3.1 情况一 若实体类中的属性使用的是驼峰命名风格而表中的字段使用的是下划线命名风格
例如实体类属性userName表中字段user_name
此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格
相当于在MyBatis中配置
3.2 情况二 若实体类中的属性和表中的字段不满足情况1 例如实体类属性name表中字段username 此时需要在实体类属性上使用TableField(username)设置属性所对应的字段名 public class User {TableId(uid)private Long id;TableField(username)private String name;private Integer age;private String email;
}4.TableLogic
4.1 逻辑删除 物理删除真实删除将对应数据从数据库中删除之后查询不到此条被删除的数据 逻辑删除假删除将对应数据中代表是否被删除字段的状态修改为“被删除状态”之后在数据库中仍旧能看到此条数据记录 使用场景可以进行数据恢复 4.2 实现逻辑删除 数据库中创建逻辑删除状态列设置默认值为0 实体类中添加逻辑删除属性 测试删除功能真正执行的是修改 public void testDeleteById(){int result userMapper.deleteById(1527472864163348482L);System.out.println(result 0 ? 删除成功 : 删除失败);System.out.println(受影响的行数为 result);
}此时执行查询方法查询的结果为自动添加条件is_deleted0
五、条件构造器
1.Wrapper介绍 Wrapper 条件构造抽象类最顶端父类 AbstractWrapper 用于查询条件封装生成 sql 的 where 条件 QueryWrapper 查询条件封装 UpdateWrapper Update 条件封装 AbstractLambdaWrapper 使用Lambda 语法 LambdaQueryWrapper 用于Lambda语法使用的查询Wrapper LambdaUpdateWrapper Lambda 更新封装Wrapper
2.QueryWrapper 组装查询条件 **执行SQL**SELECT uid AS id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL) public void test01(){//查询用户名包含a年龄在20到30之间邮箱信息不为null的用户信息QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.like(username,a).between(age,20,30).isNotNull(email);ListUser users userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}组装排序条件 **执行SQL**SELECT uid AS id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted0 ORDER BY age DESC,id ASC public void test02(){//查询用户信息按照年龄的降序排序若年龄相同则按照id升序排序QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.orderByDesc(age).orderByAsc(id);ListUser users userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}组装删除条件 **执行SQL**UPDATE t_user SET is_deleted1 WHERE is_deleted0 AND (email IS NULL) public void test03(){//删除邮箱地址为null的用户信息QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.isNull(email);int result userMapper.delete(queryWrapper);System.out.println(result 0 ? 删除成功 : 删除失败);System.out.println(受影响的行数为 result);
}条件的优先级 **执行SQL**UPDATE t_user SET user_name?, email? WHERE is_deleted0 AND (age ? AND user_name LIKE ? OR email IS NULL) public void test04(){//将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改UpdateWrapperUser updateWrapper new UpdateWrapper();updateWrapper.gt(age,20).like(username,a).or().isNull(email);User user new User();user.setName(Oz);user.setEmail(testoz6.com);int result userMapper.update(user, updateWrapper);System.out.println(result 0 ? 修改成功 : 修改失败);System.out.println(受影响的行数为 result);
}**执行SQL**UPDATE t_user SET username?, email? WHERE is_deleted0 AND (username LIKE ? AND (age ? OR email IS NULL)) public void test05(){//将用户名中包含有a并且年龄大于20或邮箱为null的用户信息修改UpdateWrapperUser updateWrapper new UpdateWrapper();updateWrapper.like(username,a).and(i-i.gt(age,20).or().isNull(email));User user new User();user.setName(Vz7797);user.setEmail(testss8o.com);int result userMapper.update(user, updateWrapper);System.out.println(result 0 ? 修改成功 : 修改失败);System.out.println(受影响的行数为 result);
}组装select子句 **执行SQL**SELECT username,age,email FROM t_user WHERE is_deleted0 public void test06(){//查询用户的用户名、年龄、邮箱信息QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.select(username,age,email);ListMapString, Object maps userMapper.selectMaps(queryWrapper);maps.forEach(System.out::println);
}实现子查询 **执行SQL**SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted0 AND (uid IN (select uid from t_user where uid 100)) public void test07(){//查询id小于等于100的用户信息QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.inSql(uid, select uid from t_user where uid 100);ListUser list userMapper.selectList(queryWrapper);list.forEach(System.out::println);
}3.UpdateWrapper UpdateWrapper不仅拥有QueryWrapper的组装条件功能还提供了set方法进行修改对应条件的数据库信息 public void test08(){//将用户名中包含有a并且年龄大于20或邮箱为null的用户信息修改UpdateWrapperUser updateWrapper new UpdateWrapper();updateWrapper.like(username,a).and( i - i.gt(age,20).or().isNull(email)).set(email,svipqq.com);int result userMapper.update(null, updateWrapper);System.out.println(result 0 ? 修改成功 : 修改失败);System.out.println(受影响的行数为 result);
}4.condition 在真正开发的过程中组装条件是常见的功能而这些条件数据来源于用户输入是可选的因此我们在组装这些条件时必须先判断用户是否选择了这些条件若选择则需要组装该条件若没有选择则一定不能组装以免影响SQL执行的结果 思路一 **执行SQL**SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted0 AND (user_name LIKE ? AND age ?) public void test09(){String username a;Integer ageBegin null;Integer ageEnd 30;QueryWrapperUser queryWrapper new QueryWrapper();if(StringUtils.isNotBlank(username)){//isNotBlank判断某个字符创是否不为空字符串、不为null、不为空白符queryWrapper.like(user_name, username);}if(ageBegin ! null){queryWrapper.ge(age, ageBegin);}if(ageEnd ! null){queryWrapper.le(age, ageEnd);}ListUser list userMapper.selectList(queryWrapper);list.forEach(System.out::println);}思路二 上面的实现方案没有问题但是代码比较复杂我们可以使用带condition参数的重载方法构建查询条件简化代码的编写 public void test10(){String username a;Integer ageBegin null;Integer ageEnd 30;QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.like(StringUtils.isNotBlank(username), user_name, username).ge(ageBegin ! null, age, ageBegin).le(ageEnd ! null, age, ageEnd);ListUser list userMapper.selectList(queryWrapper);list.forEach(System.out::println);
}5.LambdaQueryWrapper 功能等同于QueryWrapper提供了Lambda表达式的语法可以避免填错列名。 public void test11(){String username a;Integer ageBegin null;Integer ageEnd 30;LambdaQueryWrapperUser queryWrapper new LambdaQueryWrapper();queryWrapper.like(StringUtils.isNotBlank(username), User::getName, username).ge(ageBegin ! null, User::getAge, ageBegin).le(ageEnd ! null, User::getAge, ageEnd);ListUser list userMapper.selectList(queryWrapper);list.forEach(System.out::println);
}6.LambdaUpdateWrapper 功能等同于UpdateWrapper提供了Lambda表达式的语法可以避免填错列名。 public void test12(){//将用户名中包含有a并且年龄大于20或邮箱为null的用户信息修改LambdaUpdateWrapperUser updateWrapper new LambdaUpdateWrapper();updateWrapper.like(User::getName, a).and(i - i.gt(User::getAge, 20).or().isNull(User::getEmail));updateWrapper.set(User::getName, 小黑).set(User::getEmail,abcatguigu.com);int result userMapper.update(null, updateWrapper);System.out.println(resultresult);
}六、常用插件
1.分页插件 MyBatis Plus自带分页插件只要简单的配置即可实现分页功能 添加配置类MyBatisPlusConfig Configuration
MapperScan(com.atguigu.mybatisplus.mapper)
public class MyBatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();//添加分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}编写测试方法 Test
public void testPage(){//new Page()中的两个参数分别是当前页码每页显示数量PageUser page userMapper.selectPage(new Page(1, 2), null);ListUser users page.getRecords();users.forEach(System.out::println);
}2.自定义分页 上面调用的是MyBatis-Plus提供的带有分页的方法那么我们自己定义的方法如何实现分页呢 在UserMapper接口中定义一个方法 /*** 根据年龄查询用户列表分页显示 * param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位 * param age 年龄 * return */
PageUser selectPageVo(Param(page) PageUser page,Param(age) Integer age);在UserMapper.xml中编写SQL实现该方法 select idselectPageVo resultTypeUserselect id,username as name,age,email from t_user where age #{age}
/select编写测试方法 Test
public void testPageVo(){PageUser page userMapper.selectPageVo(new PageUser(1,2), 20);ListUser users page.getRecords();users.forEach(System.out::println);
}3.乐观锁 作用当要更新一条记录的时候希望这条记录没有被别人更新 乐观锁的实现方式
取出记录时获取当前 version更新时带上这个 version执行更新时 set version newVersion where version oldVersion如果 version 不对就更新失败
3.1 场景 一件商品成本价是80元售价是100元。老板先是通知小李说你去把商品价格增加50元。小李正在玩游戏耽搁了一个小时。正好一个小时后老板觉得商品价格增加到150元价格太高可能会影响销量。又通知小王你把商品价格降低30元。此时小李和小王同时操作商品后台系统。小李操作的时候系统先取出商品价格100元小王也在操作取出的商品价格也是100元。小李将价格加了50元并将10050150元存入了数据库小王将商品减了30元并将100-3070元存入了数据库。是的如果没有锁小李的操作就完全被小王的覆盖了。现在商品价格是70元比成本价低10元。几分钟后这个商品很快出售了1千多件商品老板亏1万多。
3.2 乐观锁与悲观锁 上面的故事如果是乐观锁小王保存价格前会检查下价格是否被人修改过了。如果被修改过了则重新取出的被修改后的价格150元这样他会将120元存入数据库。如果是悲观锁小李取出数据后小王只能等小李操作完之后才能对价格进行操作也会保证最终的价格是120元。
3.3 模拟修改冲突 数据库中增加商品表 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)
);添加一条数据 INSERT INTO t_product (id, NAME, price) VALUES (1, 外星人笔记本, 100);
1添加一个实体类Product Data
public class Product {private Long id;private String name;private Integer price;private Integer version;
}添加一个Mapper接口ProductMapper public interface ProductMapper extends BaseMapperProduct {}
1测试方法 Test
public void testProduct01(){//1.小李获取商品价格Product productLi productMapper.selectById(1);System.out.println(小李获取的商品价格为 productLi.getPrice());//2.小王获取商品价格Product productWang productMapper.selectById(1);System.out.println(小李获取的商品价格为 productWang.getPrice());//3.小李修改商品价格50productLi.setPrice(productLi.getPrice()50);productMapper.updateById(productLi);//4.小王修改商品价格-30productWang.setPrice(productWang.getPrice()-30);productMapper.updateById(productWang);//5.老板查询商品价格Product productBoss productMapper.selectById(1);System.out.println(老板获取的商品价格为 productBoss.getPrice());
}执行结果
3.4 乐观锁解决问题 实体类version字段添加注解Version Data
public class Product {private Long id;private String name;private Integer price;Versionprivate Integer version;
}添加乐观锁插件配置 Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();//添加分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//添加乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;
}再次执行测试方法 小李查询商品信息 SELECT id,name,price,version FROM t_product WHERE id? 小王查询商品信息 SELECT id,name,price,version FROM t_product WHERE id? 小李修改商品价格自动将version1 UPDATE t_product SET name?, price?, version? WHERE id? AND version? Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer) 小王修改商品价格此时version已更新条件不成立修改失败 UPDATE t_product SET name?, price?, version? WHERE id? AND version? Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer) 最终小王修改失败查询价格150 SELECT id,name,price,version FROM t_product WHERE id? 优化执行流程 Test
public void testProduct01(){//1.小李获取商品价格Product productLi productMapper.selectById(1);System.out.println(小李获取的商品价格为 productLi.getPrice());//2.小王获取商品价格Product productWang productMapper.selectById(1);System.out.println(小李获取的商品价格为 productWang.getPrice());//3.小李修改商品价格50productLi.setPrice(productLi.getPrice()50);productMapper.updateById(productLi);//4.小王修改商品价格-30productWang.setPrice(productWang.getPrice()-30);int result productMapper.updateById(productWang);if(result 0){//操作失败重试Product productNew productMapper.selectById(1);productNew.setPrice(productNew.getPrice()-30);productMapper.updateById(productNew);}//5.老板查询商品价格Product productBoss productMapper.selectById(1);System.out.println(老板获取的商品价格为 productBoss.getPrice());
}七、通用枚举 表中的有些字段值是固定的例如性别男或女此时我们可以使用MyBatis-Plus的通用枚举来实现 数据库表添加字段sex 创建通用枚举类型 Getter
public enum SexEnum {MALE(1, 男),FEMALE(2, 女);EnumValue //将注解所标识的属性的值存储到数据库中private int sex;private String sexName;SexEnum(Integer sex, String sexName) {this.sex sex;this.sexName sexName;}
}User实体类中添加属性sex public class User {private Long id;TableField(username)private String name;private Integer age;private String email;TableLogicprivate int isDeleted; //逻辑删除private SexEnum sex;
}配置扫描通用枚举 #MyBatis-Plus相关配置
mybatis-plus:#指定mapper文件所在的地址mapper-locations: classpath:mapper/*.xmlconfiguration:#配置日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:banner: offdb-config:#配置mp的主键策略为自增id-type: auto# 设置实体类所对应的表的统一前缀table-prefix: t_#配置类型别名所对应的包type-aliases-package: com.atguigu.mybatisplus.pojo# 扫描通用枚举的包type-enums-package: com.atguigu.mybatisplus.enums执行测试方法 Test
public void test(){User user new User();user.setName(admin);user.setAge(33);user.setSex(SexEnum.MALE);int result userMapper.insert(user);System.out.println(result:result);
}八、多数据源 适用于多种场景纯粹多库、 读写分离、 一主多从、 混合模式等 场景说明
我们创建两个库分别为mybatis_plus以前的库不动与mybatis_plus_1新建将mybatis_plus库的product表移动到mybatis_plus_1库这样每个库一张表通过一个测试用例分别获取用户数据与商品数据如果获取到说明多库模拟成功
1.创建数据库及表 创建数据库mybatis_plus_1和表product CREATE DATABASE mybatis_plus_1 /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use mybatis_plus_1;
CREATE TABLE 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)
);添加测试数据 INSERT INTO product (id, NAME, price) VALUES (1, 外星人笔记本, 100);删除mybatis_plus库中的product表 use mybatis_plus;
DROP TABLE IF EXISTS product;2.新建工程引入依赖 自行新建一个Spring Boot工程并选择MySQL驱动及Lombok依赖 引入MyBaits-Plus的依赖及多数据源的依赖
dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.1/version
/dependencydependencygroupIdcom.baomidou/groupIdartifactIddynamic-datasource-spring-boot-starter/artifactIdversion3.5.0/version
/dependency3.编写配置文件
spring:# 配置数据源信息datasource:dynamic:# 设置默认的数据源或者数据源组,默认值即为masterprimary: master# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源strict: falsedatasource:master:url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncodingutf-8useSSLfalsedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 132537slave_1:url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncodingutf-8useSSLfalsedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 1325374.创建实体类 新建一个User实体类如果数据库表名有t_前缀记得配置 Data
public class User {private Long id;private String name;private Integer age;private String email;
}新建一个实体类Product Data
public class Product {private Long id;private String name;private Integer price;private Integer version;
}5.创建Mapper及Service 新建接口UserMapper public interface UserMapper extends BaseMapperUser {}新建接口ProductMapper public interface ProductMapper extends BaseMapperProduct {}新建Service接口UserService指定操作的数据源 DS(master) //指定操作的数据源master为user表
public interface UserService extends IServiceUser {}新建Service接口ProductService指定操作的数据源 DS(slave_1)
public interface ProductService extends IServiceProduct {}自行建立Service的实现类 ...6.编写测试方法 记得在启动类中添加注解MapperScan() class TestDatasourceApplicationTests {ResourceUserService userService;ResourceProductService productService;Testvoid contextLoads() {User user userService.getById(1L);Product product productService.getById(1L);System.out.println(User user);System.out.println(Product product);}}九、MyBatisX插件 MyBatis-Plus为我们提供了强大的mapper和service模板能够大大的提高开发效率。 但是在真正开发过程中MyBatis-Plus并不能为我们解决所有问题例如一些复杂的SQL多表联查我们就需要自己去编写代码和SQL语句我们该如何快速的解决这个问题呢这个时候可以使用MyBatisX插件。 MyBatisX一款基于 IDEA 的快速开发插件为效率而生。 1.安装MyBatisX插件 打开IDEAFile- Setteings-Plugins-MyBatisX搜索栏搜索MyBatisX然后安装。 2.快速生成代码 新建一个Spring Boot项目引入依赖创建工程时记得勾选lombok及mysql驱动 dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.1/version
/dependencydependencygroupIdcom.baomidou/groupIdartifactIddynamic-datasource-spring-boot-starter/artifactIdversion3.5.0/version
/dependency配置数据源信息 spring:datasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_plus?characterEncodingutf-8useSSLfalseusername: rootpassword: 132537在IDEA中与数据库建立链接 填写数据库信息并保存 找到我们需要生成的表点击右键 填写完信息以后下一步 继续填写信息 大功告成真特么好用yyds
3.快速生成CRUD MyBaitsX可以根据我们在Mapper接口中输入的方法名快速帮我们生成对应的sql语句