SpringBoot集成MyBatisPlus
MyBatis的概念
MyBatis-Plus(简称 MP)是一个基于 MyBatis 的增强工具库,它简化了 MyBatis 的开发流程,提供了更多的便捷功能和增强特性。MyBatis-Plus 并不是替代 MyBatis,而是在 MyBatis 的基础上进行扩展和增强。 以下是 MyBatis-Plus 提供的一些主要功能和概念:
CRUD 操作支持:MyBatis-Plus 提供了丰富的 CRUD 操作方法,可以通过简单的 API 完成数据库的增删改查操作。 条件构造器(Wrapper):MyBatis-Plus 提供了强大的条件构造器,可以通过编程方式构建 SQL 查询条件,包括等于、不等于、模糊查询、排序等。 分页查询:MyBatis-Plus 支持简单的分页查询,可以轻松地进行分页操作。 代码生成器:MyBatis-Plus 提供了一个代码生成器,可以根据数据库表结构自动生成实体类、Mapper 接口和 XML 映射文件,减少手写重复代码的工作量。 通用 Service 层封装:MyBatis-Plus 还提供了通用的 Service 层封装,简化了 Service 的编写和操作。 Lambda 表达式支持:MyBatis-Plus 支持使用 Lambda 表达式进行查询条件的构建,使得代码更加简洁和易读。 自动填充:MyBatis-Plus 支持自动填充功能,在插入或更新数据时,可以自动填充指定的字段,如创建时间、更新时间等。 总之,MyBatis-Plus 是一个功能强大、提高开发效率的 MyBatis 增强工具库,通过提供丰富的功能和便捷的 API,简化了数据库操作和查询条件构造的过程。它能够极大地简化开发人员的工作量,并提供更好的开发体验。
相关常用注解
@Table:表名注解,标识实体类对应的表
相关属性:
value:执行表名 schema:用于指定数据库的 schema(模式)名称。Schema 是数据库中的一个概念,它可以用来将数据库对象(如表、视图、存储过程等)进行逻辑上的分类和隔离。不同的数据库系统对 schema 的支持和用法可能会有所不同。 当 schema 参数被指定时,MyBatis-Plus 会将对应的实体类与指定的 schema 下的数据库表进行映射。这在多个 schema 下存在相同表名的情况下非常有用,可以避免表名冲突的问题。 keepGlobalPrefix:是否保持全局的表前缀配置。默认为 false。 resultMap:指定该实体类使用的结果映射(ResultMap)的 id。 autoResultMap:是否自动生成结果映射(ResultMap)。默认为 true。
MP 会自动构建一个 resultMap 并注入到 MyBatis 里(一般用不上) 因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 Entity 字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。 prefix:表前缀,用于生成动态表名。如果设置了 prefix,生成的 SQL 语句中会将表名替换为 ${prefix}your_table_name 的形式。 suffix:表后缀,用于生成动态表名。如果设置了 suffix,生成的 SQL 语句中会将表名替换为 your_table_name${suffix} 的形式。 comment:该表的注释信息。 @TableId:标识一个字段为表的主键,主要用于字段上
@TableName ( "sys_user" )
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
}
其他相关注解及用法参考:https://baomidou.com/pages/223848/#orderby
MyBatisPlus中常用的CRUD接口
Service层中的CRUD,通用 Service CRUD 封装IService (opens new window)接口
Save
// 插入一条记录(选择字段,策略插入)
// 插入(批量)
boolean saveBatch(Collection entityList); // 插入(批量)
boolean saveBatch(Collection entityList, int batchSize); SaveOrUpdate
// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity); // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper updateWrapper); // 批量修改插入
boolean saveOrUpdateBatch(Collection entityList); // 批量修改插入
boolean saveOrUpdateBatch(Collection entityList, int batchSize); Remove
// 根据 queryWrapper 设置的条件,删除记录
boolean remove(Wrapper queryWrapper); // 根据 ID 删除
boolean removeById(Serializable id); // 根据 columnMap 条件,删除记录
boolean removeByMap(Map<String, Object> columnMap); // 删除(根据ID 批量删除)
boolean removeByIds(Collection<? extends Serializable> idList); Update
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper updateWrapper); // 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper whereWrapper); // 根据 ID 选择修改
boolean updateById(T entity); // 根据ID 批量更新
boolean updateBatchById(Collection entityList); // 根据ID 批量更新
boolean updateBatchById(Collection entityList, int batchSize); Get
// 根据 ID 查询
T getById(Serializable id); // 根据 Wrapper—(实体对象封装操作类 QueryWrapper),查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last(“LIMIT 1”)
T getOne(Wrapper queryWrapper); // 根据 Wrapper,查询一条记录
T getOne(Wrapper queryWrapper, boolean throwEx); // 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper queryWrapper); // 根据 Wrapper,查询一条记录
// V getObj(Wrapper queryWrapper, Function<? super Object, V> mapper); List
// 查询所有
// 查询列表
List list(Wrapper queryWrapper); // 查询(根据ID 批量查询)
Collection listByIds(Collection<? extends Serializable> idList); // 查询(根据 columnMap 条件)
Collection listByMap(Map<String, Object> columnMap); // 查询所有列表
List<Map<String, Object>> listMaps(); // 查询列表
List<Map<String, Object>> listMaps(Wrapper queryWrapper); // 查询全部记录
// 查询全部记录
// List listObjs(Function<? super Object, V> mapper); // 根据 Wrapper 条件,查询全部记录
List listObjs(Wrapper queryWrapper); // 根据 Wrapper 条件,查询全部记录
// List listObjs(Wrapper queryWrapper, Function<? super Object, V> mapper); Page
// 无条件分页查询
// 条件分页查询
IPage page(IPage page, Wrapper queryWrapper); // 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage page); // 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage page, Wrapper queryWrapper); Count
// 查询总记录数
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper queryWrapper); query
// 链式查询 普通
QueryChainWrapper query(); // 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper lambdaQuery(); // 示例: query().eq(“column”, value).one(); lambdaQuery().eq(Entity::getId, value).list(); update
// 链式更改 普通
UpdateChainWrapper update(); // 链式更改 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper lambdaUpdate(); // 示例: update().eq(“column”, value).remove(); lambdaUpdate().eq(Entity::getId, value).update(entity); Mapper层的CRUD,通用 CRUD 封装BaseMapper (opens new window)接口
Insert
Delete
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper wrapper);-- 实体对象封装操作类(可以为 null) // 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 ID 删除
int deleteById(Serializable id); // 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); Update
// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper whereWrapper); // 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity); Select
// 根据 ID 查询
T selectById(Serializable id); // 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper);-- 实体对象封装操作类(可以为 null) // 查询(根据ID 批量查询)
List selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 entity 条件,查询全部记录
List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); // 查询(根据 columnMap 条件)
List selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List selectObjs(@Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 entity 条件,查询全部记录(并翻页)
IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper);
ActiveRecord 模式
实体类只需继承 Model 类即可进行强大的 CRUD 操作 需要项目中已注入对应实体的BaseMapper,不使用 mapper,也需要定义对应的mapper类,Mybatis-Plus通过 mapper 获取到表的结构; 如果实体类继承了 Model 类,那么在对应的 mapper 类中定义的方法会覆盖 Model 类中同名的方法。因此,如果你在 mapper 类中定义了一个与 Model 类中同名的方法,那么最终调用时会执行 mapper 类中的方法,而不是 Model 类中的方法。
class User extends Model < User > {
}
User user = new User();
user.insert();
user.selectAll();
user.updateById();
user.deleteById();
// ...
SimpleQuery 工具类
对selectList查询后的结果用Stream流进行了一些封装,使其可以返回一些指定结果,简洁了api的调用 需要项目中已注入对应实体的BaseMapper keyMap
// 查询表内记录,封装返回为Map<属性,实体>
Map<A, E> keyMap(LambdaQueryWrapper wrapper, SFunction<E, A> sFunction, Consumer… peeks); // 查询表内记录,封装返回为Map<属性,实体>,考虑了并行流的情况
Map<A, E> keyMap(LambdaQueryWrapper wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer… peeks); map
// 查询表内记录,封装返回为Map<属性,属性>
Map<A, P> map(LambdaQueryWrapper wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, Consumer… peeks); // 查询表内记录,封装返回为Map<属性,属性>,考虑了并行流的情况
Map<A, P> map(LambdaQueryWrapper wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, boolean isParallel, Consumer… peeks); group(sFunction-封装后list中的元素\downstream-下游收集器\isParallel-为true时底层使用并行流执行\peeks-可叠加的后续操作)
// 查询表内记录,封装返回为Map<属性,List<实体>>
Map<K, List> group(LambdaQueryWrapper wrapper, SFunction<T, A> sFunction, Consumer… peeks); // 查询表内记录,封装返回为Map<属性,List<实体>>,考虑了并行流的情况
Map<K, List> group(LambdaQueryWrapper wrapper, SFunction<T, K> sFunction, boolean isParallel, Consumer… peeks); // 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>
M group(LambdaQueryWrapper wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, Consumer… peeks); // 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>,考虑了并行流的情况
M group(LambdaQueryWrapper wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, boolean isParallel, Consumer… peeks); list(sFunction-封装后list中的元素\isParallel-为true时底层使用并行流执行\peeks-可叠加的后续操作)
// 查询表内记录,封装返回为List<属性>
List list(LambdaQueryWrapper wrapper, SFunction<E, A> sFunction, Consumer… peeks); // 查询表内记录,封装返回为List<属性>,考虑了并行流的情况
List list(LambdaQueryWrapper wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer… peeks);
class SimpleQueryTest extends BaseDbTest < EntityMapper > {
@Test
void testList ( ) {
List < Long > entityIds = SimpleQuery . list ( lambdaQuery ( ) , Entity :: getId ) ;
assertThat ( entityIds) . containsExactly ( 1L , 2L ) ;
List < String > names = SimpleQuery . list ( lambdaQuery ( ) , Entity :: getName ,
e -> Optional . ofNullable ( e. getName ( ) )
. map ( String :: toUpperCase )
. ifPresent ( e:: setName ) ) ;
assertThat ( names) . containsExactly ( "RUBEN" , null ) ;
}
@Test
void testMap ( ) {
Map < Long , Entity > idEntityMap = SimpleQuery . keyMap (
Wrappers . < Entity > lambdaQuery ( ) . eq ( Entity :: getId , 1L ) , Entity :: getId ) ;
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
entity. setName ( "ruben" ) ;
Assert . isTrue ( idEntityMap. equals ( Collections . singletonMap ( 1L , entity) ) , "Ops!" ) ;
Map < Long , String > idNameMap = SimpleQuery . map ( lambdaQuery ( ) , Entity :: getId , Entity :: getName ) ;
Map < Long , String > map = new HashMap < > ( 1 << 2 ) ;
map. put ( 1L , "ruben" ) ;
map. put ( 2L , null ) ;
Assert . isTrue ( idNameMap. equals ( map) , "Ops!" ) ;
}
@Test
void testGroup ( ) {
doTestAutoCommit ( m -> {
Entity entity = new Entity ( ) ;
entity. setId ( 3L ) ;
entity. setName ( "ruben" ) ;
m. insert ( entity) ;
} ) ;
Map < String , List < Entity > > nameUsersMap = SimpleQuery . group ( lambdaQuery ( ) , Entity :: getName ) ;
Map < String , List < Entity > > map = new HashMap < > ( 1 << 2 ) ;
Entity chao = new Entity ( ) ;
chao. setId ( 2L ) ;
chao. setName ( null ) ;
map. put ( null , Collections . singletonList ( chao) ) ;
Entity ruben = new Entity ( ) ;
ruben. setId ( 1L ) ;
ruben. setName ( "ruben" ) ;
Entity ruben2 = new Entity ( ) ;
ruben2. setId ( 3L ) ;
ruben2. setName ( "ruben" ) ;
map. put ( "ruben" , Arrays . asList ( ruben, ruben2) ) ;
Assert . isTrue ( nameUsersMap. equals ( map) , "Ops!" ) ;
Map < String , List < Long > > nameIdMap = SimpleQuery . group ( lambdaQuery ( ) , Entity :: getName ,
mapping ( Entity :: getId , toList ( ) ) ) ;
assertThat ( nameIdMap) . containsExactly ( entry ( null , Arrays . asList ( 2L ) ) , entry ( "ruben" , Arrays . asList ( 1L , 3L ) ) ) ;
Map < String , Long > nameCountMap = SimpleQuery . group ( lambdaQuery ( ) , Entity :: getName , counting ( ) ) ;
assertThat ( nameCountMap) . containsExactly ( entry ( null , 1L ) , entry ( "ruben" , 2L ) ) ;
}
@Override
protected String tableDataSql ( ) {
return "insert into entity(id,name) values(1, 'ruben'), (2, null);" ;
}
@Override
protected List < String > tableSql ( ) {
return Arrays . asList ( "drop table if exists entity" , "CREATE TABLE IF NOT EXISTS entity (" +
"id BIGINT NOT NULL," +
"name VARCHAR(30) NULL DEFAULT NULL," +
"PRIMARY KEY (id))" ) ;
}
}
Db类
使用静态调用的方式,执行CRUD方法,避免Spring环境下Service循环注入、简洁代码,提升效率 基本使用:
// 根据id查询
List list = Db.listByIds(Arrays.asList(1L, 2L), Entity.class); // 根据条件构造器查询
List list = Db.list(Wrappers.lambdaQuery(Entity.class)); // 批量根据id更新
boolean isSuccess = Db.updateBatchById(list);
class DbTest extends BaseDbTest < EntityMapper > {
@Test
void testSave ( ) {
Entity entity = new Entity ( ) ;
entity. setName ( "ruben" ) ;
boolean isSuccess = Db . save ( entity) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( 3L , Db . count ( Entity . class ) ) ;
}
@Test
void testSaveBatch ( ) {
List < Entity > list = Arrays . asList ( new Entity ( ) , new Entity ( ) ) ;
boolean isSuccess = Db . saveBatch ( list) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( 4 , Db . count ( Entity . class ) ) ;
}
@Test
void testSaveOrUpdateBatch ( ) {
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
entity. setName ( "cat" ) ;
List < Entity > list = Arrays . asList ( new Entity ( ) , entity) ;
boolean isSuccess = Db . saveOrUpdateBatch ( list) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( 3 , Db . count ( Entity . class ) ) ;
}
@Test
void testRemoveById ( ) {
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
boolean isSuccess = Db . removeById ( entity) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( 1 , Db . count ( Entity . class ) ) ;
isSuccess = Db . removeById ( 2L , Entity . class ) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( 0 , Db . count ( Entity . class ) ) ;
}
@Test
void testUpdateById ( ) {
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
entity. setName ( "bee bee I'm a sheep" ) ;
boolean isSuccess = Db . updateById ( entity) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( "bee bee I'm a sheep" , Db . getById ( 1L , Entity . class ) . getName ( ) ) ;
}
@Test
void testUpdate ( ) {
boolean isSuccess = Db . update ( Wrappers . lambdaUpdate ( Entity . class ) . eq ( Entity :: getId , 1L ) . set ( Entity :: getName , "be better" ) ) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( "be better" , Db . getById ( 1L , Entity . class ) . getName ( ) ) ;
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
entity. setName ( "bee bee I'm a sheep" ) ;
isSuccess = Db . update ( entity, Wrappers . lambdaQuery ( Entity . class ) . eq ( Entity :: getId , 1L ) ) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( "bee bee I'm a sheep" , Db . getById ( 1L , Entity . class ) . getName ( ) ) ;
}
@Test
void testUpdateBatchById ( ) {
Entity sheep = new Entity ( ) ;
sheep. setId ( 1L ) ;
sheep. setName ( "bee bee I'm a sheep" ) ;
Entity ruben = new Entity ( ) ;
ruben. setId ( 2L ) ;
ruben. setName ( "rabbit" ) ;
boolean isSuccess = Db . updateBatchById ( Arrays . asList ( sheep, ruben) ) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( "bee bee I'm a sheep" , Db . getById ( 1L , Entity . class ) . getName ( ) ) ;
Assertions . assertEquals ( "rabbit" , Db . getById ( 2L , Entity . class ) . getName ( ) ) ;
}
@Test
void testRemove ( ) {
boolean isSuccess = Db . remove ( Wrappers . lambdaQuery ( Entity . class ) . eq ( Entity :: getId , 1L ) ) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( 1 , Db . count ( Entity . class ) ) ;
}
@Test
void testRemoveByIds ( ) {
boolean isSuccess = Db . removeByIds ( Arrays . asList ( 1L , 2L ) , Entity . class ) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( 0 , Db . count ( Entity . class ) ) ;
}
@Test
void testRemoveByMap ( ) {
boolean isSuccess = Db . removeByMap ( Collections . singletonMap ( "id" , 1L ) , Entity . class ) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( 1 , Db . count ( Entity . class ) ) ;
}
@Test
void testSaveOrUpdate ( ) {
Entity entity = new Entity ( ) ;
entity. setId ( null ) ;
entity. setName ( "bee bee I'm a sheep" ) ;
boolean isSuccess = Db . saveOrUpdate ( entity) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( "bee bee I'm a sheep" , Db . getById ( entity. getId ( ) , Entity . class ) . getName ( ) ) ;
entity. setName ( "be better" ) ;
isSuccess = Db . saveOrUpdate ( entity, Wrappers . lambdaQuery ( Entity . class ) . eq ( Entity :: getId , entity. getId ( ) ) ) ;
Assertions . assertTrue ( isSuccess) ;
Assertions . assertEquals ( "be better" , Db . getById ( entity. getId ( ) , Entity . class ) . getName ( ) ) ;
}
@Test
void testGetOne ( ) {
LambdaQueryWrapper < Entity > wrapper = Wrappers . lambdaQuery ( Entity . class ) ;
Assertions . assertThrows ( TooManyResultsException . class , ( ) -> Db . getOne ( wrapper) ) ;
Entity one = Db . getOne ( wrapper, false ) ;
Assertions . assertNotNull ( one) ;
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
one = Db . getOne ( entity) ;
Assertions . assertNotNull ( one) ;
}
@Test
void testListByMap ( ) {
Map < String , Object > map = new HashMap < > ( ) ;
map. put ( "id" , 1L ) ;
List < Entity > list = Db . listByMap ( map, Entity . class ) ;
Assertions . assertEquals ( 1 , list. size ( ) ) ;
Assertions . assertEquals ( "ruben" , list. get ( 0 ) . getName ( ) ) ;
}
@Test
void testByIds ( ) {
List < Entity > list = Db . listByIds ( Arrays . asList ( 1L , 2L ) , Entity . class ) ;
Assertions . assertEquals ( 2 , list. size ( ) ) ;
}
@Test
void testGetMap ( ) {
Map < String , Object > map = Db . getMap ( Wrappers . lambdaQuery ( Entity . class ) ) ;
Assertions . assertNotNull ( map) ;
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
map = Db . getMap ( entity) ;
Assertions . assertNotNull ( map) ;
}
@Test
void testList ( ) {
List < Entity > list = Db . list ( Wrappers . lambdaQuery ( Entity . class ) ) ;
Assertions . assertEquals ( 2 , list. size ( ) ) ;
list = Db . list ( Entity . class ) ;
Assertions . assertEquals ( 2 , list. size ( ) ) ;
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
list = Db . list ( entity) ;
Assertions . assertEquals ( 1 , list. size ( ) ) ;
}
@Test
void testListMaps ( ) {
List < Map < String , Object > > list = Db . listMaps ( Wrappers . lambdaQuery ( Entity . class ) ) ;
Assertions . assertEquals ( 2 , list. size ( ) ) ;
list = Db . listMaps ( Entity . class ) ;
Assertions . assertEquals ( 2 , list. size ( ) ) ;
Entity entity = new Entity ( ) ;
entity. setId ( 1L ) ;
list = Db . listMaps ( entity) ;
Assertions . assertEquals ( 1 , list. size ( ) ) ;
}
@Test
void testListObjs ( ) {
List < Entity > list = Db . listObjs ( Entity . class ) ;
Assertions . assertEquals ( 2 , list. size ( ) ) ;
List < Long > objectList = Db . listObjs ( Wrappers . lambdaQuery ( Entity . class ) , Entity :: getId ) ;
Assertions . assertEquals ( 2 , objectList. size ( ) ) ;
List < String > names = Db . listObjs ( Entity . class , Entity :: getName ) ;
Assertions . assertArrayEquals ( new String [ ] { "ruben" , "chocolate" } , names. toArray ( ) ) ;
}
@Override
protected List < Interceptor > interceptors ( ) {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor ( ) ;
interceptor. addInnerInterceptor ( new PaginationInnerInterceptor ( DbType . SQLITE ) ) ;
return Collections . singletonList ( interceptor) ;
}
@Test
void testPageMaps ( ) {
Page < Map < String , Object > > page = Db . pageMaps ( new Page < > ( 1 , 1 ) , Entity . class ) ;
Assertions . assertEquals ( 2 , page. getTotal ( ) ) ;
Assertions . assertEquals ( Db . listMaps ( new Page < > ( 1 , 1 , false ) , Entity . class ) . size ( ) , page. getRecords ( ) . size ( ) ) ;
page = Db . pageMaps ( new Page < > ( 1 , 1 ) , Wrappers . lambdaQuery ( Entity . class ) ) ;
Assertions . assertEquals ( 1 , page. getRecords ( ) . size ( ) ) ;
Assertions . assertEquals ( Db . listMaps ( new Page < > ( 1 , 1 , false ) , Wrappers . lambdaQuery ( Entity . class ) ) . size ( ) , page. getRecords ( ) . size ( ) ) ;
}
@Test
void testPage ( ) {
IPage < Entity > page = Db . page ( new Page < > ( 1 , 1 ) , Entity . class ) ;
Assertions . assertEquals ( 2 , page. getTotal ( ) ) ;
Assertions . assertEquals ( Db . list ( new Page < Entity > ( 1 , 1 ) , Entity . class ) . size ( ) , page. getRecords ( ) . size ( ) ) ;
page = Db . page ( new Page < > ( 1 , 1 ) , Wrappers . lambdaQuery ( Entity . class ) ) ;
Assertions . assertEquals ( 1 , page. getRecords ( ) . size ( ) ) ;
Assertions . assertEquals ( Db . list ( new Page < Entity > ( 1 , 1 ) , Wrappers . lambdaQuery ( Entity . class ) ) . size ( ) , page. getRecords ( ) . size ( ) ) ;
}
@Test
void testChain ( ) {
QueryChainWrapper < Entity > query = Db . query ( Entity . class ) ;
List < Entity > list = query. eq ( "id" , 1L ) . list ( ) ;
Assertions . assertEquals ( 1 , list. size ( ) ) ;
LambdaQueryChainWrapper < Entity > lambdaQuery = Db . lambdaQuery ( Entity . class ) ;
list = lambdaQuery. eq ( Entity :: getId , 1L ) . list ( ) ;
Assertions . assertEquals ( 1 , list. size ( ) ) ;
UpdateChainWrapper < Entity > update = Db . update ( Entity . class ) ;
update. eq ( "id" , 1L ) . set ( "name" , "bee bee I'm a sheep" ) . update ( ) ;
Assertions . assertEquals ( "bee bee I'm a sheep" , lambdaQuery. eq ( Entity :: getId , 1L ) . one ( ) . getName ( ) ) ;
LambdaUpdateChainWrapper < Entity > lambdaUpdate = Db . lambdaUpdate ( Entity . class ) ;
lambdaUpdate. eq ( Entity :: getId , 1L ) . set ( Entity :: getName , "be better" ) . update ( ) ;
Assertions . assertEquals ( "be better" , lambdaQuery. eq ( Entity :: getId , 1L ) . one ( ) . getName ( ) ) ;
}
@Test
void testGetObj ( ) {
String name = Db . getObj ( Wrappers . lambdaQuery ( Entity . class ) . eq ( Entity :: getId , 1L ) , Entity :: getName ) ;
Assertions . assertEquals ( "ruben" , name) ;
}
@Override
protected String tableDataSql ( ) {
return "insert into entity(id,name) values(1,'ruben'),(2,'chocolate');" ;
}
@Override
protected List < String > tableSql ( ) {
return Arrays . asList ( "drop table if exists entity" , "CREATE TABLE IF NOT EXISTS entity (" +
"id BIGINT NOT NULL," +
"name VARCHAR(30) NULL DEFAULT NULL," +
"PRIMARY KEY (id))" ) ;
}
}
SpringBoot项目中使用MyBatisplus
< dependency>
< groupId> com.baomidou</ groupId>
< artifactId> mybatis-plus-boot-starter</ artifactId>
< version> 3.1.0</ version>
</ dependency>
配置MyBatisplus,主要是配置数据源信息以及整合MyBatisplus的配置
datasource :
url : localhost: 3306/liu
spring :
datasource :
driver-class-name : com.mysql.cj.jdbc.Driver
url : jdbc: mysql: //${ datasource.url} ? useUnicode=true&characterEncoding=UTF8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=UTC
username : root
password : root
hikari :
maximum-pool-size : 10
max-lifetime : 1770000
mybatis :
type-aliases-package : com.example.springboot_learn.entity
configuration :
map-underscore-to-camel-case : true
mapper-locations :
- classpath: mapper/*.xml
mybatis-plus :
mapper-locations : classpath: mapper/*.xml
global-config :
db-config :
id-type : auto
field-strategy : NOT_EMPTY
db-type : MYSQL
configuration :
map-underscore-to-camel-case : true
call-setters-on-nulls : true
定义MyBatisPlus的配置类,引入相关的插件
@Configuration
public class MybatisPlusConfig {
@Bean
public PerformanceInterceptor performanceInterceptor ( ) {
return new PerformanceInterceptor ( ) ;
}
@Bean
public PaginationInterceptor paginationInterceptor ( ) {
return new PaginationInterceptor ( ) ;
}
}
create table book
(
id bigint auto_increment
primary key ,
name varchar ( 20 ) null ,
code varchar ( 20 ) null ,
bid_price decimal ( 20 , 4 ) null
) ;
创建一个实体类,并使用@TableName绑定数据库中的book表:
@TableName ( "book" )
@Data
public class Book {
@TableId ( type = IdType . AUTO )
private Long id;
private String code;
private String name;
private BigDecimal bidPrice;
}
创建mapper层的接口,继承BaseMapper
@Mapper
public interface BookMapper extends BaseMapper < Book > {
}
创建service层的接口与实现类,接口继承IService接口;实现类实现前面的接口,并继承ServiceImpl类
public interface BookService extends IService < Book > { }
@Service
public class BookServiceImpl extends ServiceImpl < BookMapper , Book > implements BookService {
}
@RestController
@RequestMapping ( "/book" )
public class TestBookController {
@Resource
private BookService bookService;
private final static Logger logger = LoggerFactory . getLogger ( TestBookController . class ) ;
@RequestMapping ( "/getById" )
public JsonResult < Book > getInfo ( @RequestParam ( value = "id" ) String id) {
Book book = bookService. getById ( id) ;
return new JsonResult < > ( book) ;
}
@RequestMapping ( "/getList" )
public JsonResult < List > getList ( ) {
List < Book > bookList = bookService. list ( ) ;
return new JsonResult < > ( bookList) ;
}
@RequestMapping ( "/saveInfo" )
public void saveInfo ( ) {
Book book = new Book ( ) ;
book. setName ( "圣龙传说第1卷" ) ;
book. setCode ( "s1" ) ;
book. setBidPrice ( new BigDecimal ( 18.98 ) ) ;
bookService. save ( book) ;
}
@RequestMapping ( "/getInfoListPage" )
public IPage < Book > getInfoListPage ( ) {
IPage < Book > page = new Page < > ( ) ;
page. setCurrent ( 5 ) ;
page. setSize ( 10 ) ;
page = bookService. page ( page) ;
return page;
}
@RequestMapping ( "/saveInfoList" )
public void saveInfoList ( ) {
List < Book > list = new ArrayList < > ( ) ;
for ( int i = 0 ; i < 100 ; i++ ) {
Book book = new Book ( ) ;
book. setName ( "圣龙传说第" + i+ "卷" ) ;
book. setCode ( String . valueOf ( i) ) ;
book. setBidPrice ( new BigDecimal ( 18.98 ) ) ;
list. add ( book) ;
}
bookService. saveBatch ( list) ;
}
@RequestMapping ( "/getListMap" )
public JsonResult < Collection < Book > > getListMap ( @RequestParam ( value = "bidPrice" ) String bidPrice) {
Map < String , Object > map = new HashMap < > ( ) ;
map. put ( "bid_price" , new BigDecimal ( bidPrice) ) ;
Collection < Book > list = bookService. listByMap ( map) ;
return new JsonResult < > ( list) ;
}
@RequestMapping ( "/updateInfo" )
public void updateInfo ( ) {
Book book = new Book ( ) ;
book. setId ( 10L ) ;
book. setName ( "圣龙传说之我是帅哥" ) ;
book. setBidPrice ( new BigDecimal ( 19.98 ) ) ;
bookService. updateById ( book) ;
}
@RequestMapping ( "/saveOrUpdateInfo" )
public void saveOrUpdate ( ) {
Book book = new Book ( ) ;
book. setId ( 2L ) ;
book. setBidPrice ( new BigDecimal ( 99.98 ) ) ;
bookService. saveOrUpdate ( book) ;
}
@RequestMapping ( "/deleteInfo" )
public void deleteInfo ( String id) {
bookService. removeById ( id) ;
}
@RequestMapping ( "/deleteInfoList" )
public void deleteInfoList ( ) {
List < String > list = new ArrayList < > ( ) ;
list. add ( "99" ) ;
list. add ( "98" ) ;
bookService. removeByIds ( list) ;
}
@RequestMapping ( "/deleteInfoMap" )
public void deleteInfoMap ( ) {
Map < String , Object > map = new HashMap < > ( ) ;
map. put ( "id" , "97" ) ;
bookService. removeByMap ( map) ;
}
}
条件构造器 Wapper
声明:不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输
wrapper 很重 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场) 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作 拒绝接受任何关于 RPC 传输 Wrapper 报错相关的 issue 甚至 pr
AbstractWrapper
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类,用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件 entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age) eq:指定一个字段为什么值
eq(R column, Object val) eq(boolean condition, R column, Object val) 例子:eq(“name”, “老王”)—>name = ‘老王’ allEq:全部eq(或个别isNull)
方法
allEq(Map<R, V> params) allEq(Map<R, V> params, boolean null2IsNull) allEq(boolean condition, Map<R, V> params, boolean null2IsNull) 参数
params : key为数据库字段名,value为字段值 null2IsNull : 为true则在map的value为null时调用 isNull 方法,为false时则忽略value为null的 例子:
例1: allEq({id:1,name:“老王”,age:null})—>id = 1 and name = ‘老王’ and age is null 例2: allEq({id:1,name:“老王”,age:null}, false)—>id = 1 and name = ‘老王’ ne:不等于
用法:
例子:ne(“name”,“圣龙传说”); gt:大于 ge:大于等于 lt:小于 le:小于等于 between:在。。之间 notBetween:不在。。之间 like:LIKE ‘%值%’ notLike:NOT LIKE ‘%值%’ likeLeft:LIKE ‘%值’ likeRight:LIKE ‘值%’ notLikeLeft:NOT LIKE ‘%值’ notLikeRight:NOT LIKE ‘值%’ isNull:字段 IS NULL isNotNull in:字段 IN (value.get(0), value.get(1), …)
用法:
in(R column, Collection<?> value) in(boolean condition, R column, Collection<?> value) 例子:in(“age”,{1,2,3})—>age in (1,2,3) notIn:字段 NOT IN (value.get(0), value.get(1), …) inSql:字段 IN ( sql语句 )
用法:
inSql(R column, String inValue) inSql(boolean condition, R column, String inValue) 例子
例: inSql(“age”, “1,2,3,4,5,6”)—>age in (1,2,3,4,5,6) 例: inSql(“id”, “select id from table where id < 3”)—>id in (select id from table where id < 3) notInSql:字段 NOT IN ( sql语句 ) groupBy:分组:GROUP BY 字段, …
用法:
groupBy(R… columns) groupBy(boolean condition, R… columns) 例子:groupBy(“id”, “name”)—>group by id,name orderByAsc
排序:ORDER BY 字段, … ASC 用法:
orderByAsc(R… columns) orderByAsc(boolean condition, R… columns) 例子:orderByAsc(“id”, “name”)—>order by id ASC,name ASC orderByDesc
orderBy:排序:ORDER BY 字段, … having:HAVING ( sql语句 )
用法:
having(String sqlHaving, Object… params) having(boolean condition, String sqlHaving, Object… params) 例子:having(“sum(age) > 10”)—>having sum(age) > 10 func:func 方法(主要方便在出现if…else下调用不同方法能不断链)
用法:
func(Consumer consumer) func(boolean condition, Consumer consumer) 例子
func(i -> if(true) {i.eq(“id”, 1)} else {i.ne(“id”, 1)}) or:拼接 OR
用法:
or() or(boolean condition) 例子
eq(“id”,1).or().eq(“name”,“老王”)—>id = 1 or name = ‘老王’ or(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>or (name = ‘李白’ and status <> ‘活着’) 主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接) and:AND 嵌套
用法:
and(Consumer consumer) and(boolean condition, Consumer consumer) and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>and (name = ‘李白’ and status <> ‘活着’) nested:正常嵌套 不带 AND 或者 OR
用法:
nested(Consumer consumer) nested(boolean condition, Consumer consumer) nested(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>(name = ‘李白’ and status <> ‘活着’) apply:拼接 sql
用法:
apply(String applySql, Object… params) apply(boolean condition, String applySql, Object… params) 例子
apply(“id = 1”)—>id = 1 apply(“date_format(dateColumn,‘%Y-%m-%d’) = ‘2008-08-08’”)—>date_format(dateColumn,‘%Y-%m-%d’) = ‘2008-08-08’") apply(“date_format(dateColumn,‘%Y-%m-%d’) = {0}”, “2008-08-08”)—>date_format(dateColumn,‘%Y-%m-%d’) = ‘2008-08-08’") 该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有! last:无视优化规则直接拼接到 sql 的最后
用法:
last(String lastSql) last(boolean condition, String lastSql) 例子:
exists:拼接 EXISTS ( sql语句 )
用法:
exists(String existsSql) exists(boolean condition, String existsSql) 例子:exists(“select id from table where age = 1”)—>exists (select id from table where age = 1) notExists
集成MyBatisplus时使用lambda语法
@RestController
@RequestMapping ( "/book2" )
public class TestMyBatisPlusLambda {
@Resource
private BookService bookService;
@RequestMapping ( "/getInfoListPlus" )
public Map < String , Object > getInfoListPage ( ) {
Map < String , Object > result = new HashMap < > ( ) ;
QueryWrapper < Book > queryWrapper1 = new QueryWrapper < > ( ) ;
queryWrapper1. lambda ( ) . eq ( Book :: getName , "斗破苍穹" ) ;
List < Book > bookList1 = bookService. list ( queryWrapper1) ;
result. put ( "bookName" , bookList1) ;
QueryWrapper < Book > queryWrapper2 = new QueryWrapper < > ( ) ;
queryWrapper2. lambda ( ) . gt ( Book :: getCode , 10 ) ;
queryWrapper2. lambda ( ) . le ( Book :: getCode , 20 ) ;
List < Book > bookList2 = bookService. list ( queryWrapper2) ;
result. put ( "bookCode10to20" , bookList2) ;
QueryWrapper < Book > queryWrapper3 = new QueryWrapper < > ( ) ;
queryWrapper3. lambda ( ) . like ( Book :: getName , "苍穹" ) ;
queryWrapper3. lambda ( ) . orderByDesc ( Book :: getCode ) ;
List < Book > bookList3 = bookService. list ( queryWrapper3) ;
result. put ( "bookNameFilter" , bookList3) ;
IPage < Book > page = new Page < > ( ) ;
page. setCurrent ( 1 ) ;
page. setSize ( 5 ) ;
QueryWrapper < Book > queryWrapper5 = new QueryWrapper < > ( ) ;
queryWrapper5. lambda ( ) . isNotNull ( Book :: getName ) ;
page = bookService. page ( page, queryWrapper5) ;
result. put ( "bookPage" , page) ;
return result;
}
@RequestMapping ( "/updateInfoListPlus" )
public JsonResult updateInfo ( ) {
UpdateWrapper < Book > updateWrapper1 = new UpdateWrapper < > ( ) ;
updateWrapper1. lambda ( ) . set ( Book :: getBidPrice , new BigDecimal ( 22.987 ) ) . eq ( Book :: getName , "斗破苍穹" ) ;
Boolean isUpdate = bookService. update ( new Book ( ) , updateWrapper1) ;
System . out. println ( isUpdate) ;
return new JsonResult ( ) ;
}
}
通过上面的例子可以知道MyBatisplus中的lambda的一些基本使用,通过强大的条件构造器queryWrapper、updateWrapper进行sql语句的构造
1.QueryWrapper: Entity 对象封装操作类 2.UpdateWrapper : Update 条件封装,用于Entity对象更新操作