学习视频 强烈推荐 :https://www.bilibili.com/video/BV17E411N7KN/?spm_id_from=333.788.videocard.0
入门:https://www.imooc.com/learn/1130
进阶:https://www.imooc.com/learn/1171
动态数据源参考文档:《dynamic-datasource-动态数据源.md》
简介 MyBatis 增强工具
注意:项目中引入 mybatis 或者 mybatis-plus,不能同时引入
参考 官方:MyBatis-Plus (baomidou.com)
示例:https://github.com/baomidou/mybatis-plus-samples
!!!更新日志
:CHANGELOG.md · baomidou/mybatis-plus - 码云 - 开源中国 (gitee.com)
springboot application.yml 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 mybatis-plus: mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml global-config: banner: false db-config: id-type: ASSIGN_ID table-underline: true configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl call-setters-on-nulls: true
sql debug 日志输出 1 2 3 logging: level: com.baomidou.mybatisplus: DEBUG
注解 TableField 模糊匹配 https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/SqlCondition.java
1 2 3 4 @TableField(condition = SqlCondition.LIKE) @TableField(condition = SqlCondition.LIKE_LEFT) @TableField(condition = SqlCondition.LIKE_RIGHT)
1 QueryWrapper<UnitInfo> queryWrapper = new QueryWrapper <>(unitInfo);
CRUD 接口 Service CRUD 接口 save 1 2 3 4 5 6 7 boolean save (T entity) ;boolean saveBatch (Collection<T> entityList) ;boolean saveBatch (Collection<T> entityList, int batchSize) ;
saveOrUpdate 1 2 3 4 5 6 7 8 9 boolean saveOrUpdate (T entity) ;userService.saveOrUpdate(new User ().setName("limei" ).setAge(16 ).setId(1429425704274059265L )); boolean saveOrUpdateBatch (Collection<T> entityList) ;boolean saveOrUpdateBatch (Collection<T> entityList, int batchSize) ;
remove 1 2 3 4 5 6 7 8 boolean remove (Wrapper<T> queryWrapper) ;boolean removeById (Serializable id) ;boolean removeByMap (Map<String, Object> columnMap) ;boolean removeByIds (Collection<? extends Serializable> idList) ;
update 1 2 3 4 5 6 7 8 9 10 11 12 13 14 boolean update (Wrapper<T> updateWrapper) ;boolean update (T updateEntity, Wrapper<T> whereWrapper) ;boolean updateById (T entity) ;boolean updateBatchById (Collection<T> entityList) ;boolean updateBatchById (Collection<T> entityList, int batchSize) ;this .lambdaUpdate().set(Event::getStatus, nodeStatus).set(Event::getActStatus, status).eq(Event::getId, event.getId()).update();this .update(new LambdaUpdateWrapper <Event>().set(Event::getProcInstId, procInstId).eq(Event::getId, event.getId()));
Get 1 2 3 4 5 6 7 8 9 10 T getById (Serializable id) ; T getOne (Wrapper<T> queryWrapper) ; T getOne (Wrapper<T> queryWrapper, boolean throwEx) ; Map<String, Object> getMap (Wrapper<T> queryWrapper) ; <V> V getObj (Wrapper<T> queryWrapper, Function<? super Object, V> mapper) ;
List 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 List<T> list () ; List<T> list (Wrapper<T> queryWrapper) ; Collection<T> listByIds (Collection<? extends Serializable> idList) ; Collection<T> listByMap (Map<String, Object> columnMap) ; List<Map<String, Object>> listMaps () ; List<Map<String, Object>> listMaps (Wrapper<T> queryWrapper) ; List<Object> listObjs () ; <V> List<V> listObjs (Function<? super Object, V> mapper) ; List<Object> listObjs (Wrapper<T> queryWrapper) ; <V> List<V> listObjs (Wrapper<T> queryWrapper, Function<? super Object, V> mapper) ;
Page 1 2 3 4 5 6 7 8 IPage<T> page (IPage<T> page) ; IPage<T> page (IPage<T> page, Wrapper<T> queryWrapper) ; IPage<Map<String, Object>> pageMaps (IPage<T> page) ; IPage<Map<String, Object>> pageMaps (IPage<T> page, Wrapper<T> queryWrapper) ;
Count 1 2 3 4 int count () ;int count (Wrapper<T> queryWrapper) ;
示例 更新数据-lambdaUpdate 根据多个条件更新实体 1 this .waterFacilityService.lambdaUpdate().eq(WaterFacility::getFacilityid, waterFacility.getFacilityid()).eq(WaterFacility::getFacilityname, facilityname_old).update(waterFacility);
根据 ID 更新指定字段 1 2 3 this .formeventService.lambdaUpdate().set(Formevent::getDocmenturl, pdfpath).eq(Formevent::getId, formevent.getId()).update();this .formeventService.lambdaUpdate().set(SysUser::getOrgCode, null ).eq(SysUser::getId, userId).update();
删除数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 this .formfieldNodeService.removeById(id);List<String> ids = new ArrayList <String>(){{ add("1" ); add("2" ); }}; this .formfieldNodeService.removeByIds(ids); this .formfieldNodeService.remove(new LambdaQueryWrapper <FormfieldNode>().eq(FormfieldNode::getFieldid, fieldid));Map<String, Object> map = new HashMap <>(); map.put("FIELDID" , fieldid); this .formfieldNodeService.removeByMap(map);
查询 根据 ID 查询 1 2 3 getById(id) actBusinessService.list(new LambdaQueryWrapper <ActBusiness>().eq(ActBusiness::getProcDefId,id));
根据多个条件查询一个 1 2 3 4 WaterFacility byId = waterFacilityService.lambdaQuery().eq(WaterFacility::getFacilityid, facilityid) .eq(WaterFacility::getFacilityname, facilityname).one(); actBusinessService.getOne(new LambdaQueryWrapper <ActBusiness>().eq(ActBusiness::getTableId,tableId).last("limit 1" ));
获取一个 1 actBusinessService.getOne(new LambdaQueryWrapper <ActBusiness>().eq(ActBusiness::getTableId,tableId).last("limit 1" ));
模糊查询匹配多个字段 1 2 3 4 userWrapper.and( wrapper -> wrapper.like("name" , keywords).or().like("address" , keywords) );
Mapper CRUD 接口
说明:
通用 CRUD 封装 BaseMapper 接口,为 Mybatis-Plus
启动时自动解析实体表关系映射转换为 Mybatis
内部对象注入容器
泛型 T
为任意实体对象
参数 Serializable
为任意类型主键 Mybatis-Plus
不推荐使用复合主键约定每一张表都有自己的唯一 id
主键
对象 Wrapper
为 条件构造器
增加 1 2 3 4 5 6 7 8 9 10 11 int insert (T entity) ;User user = new User ();user.setName("小羊" ); user.setAge(3 ); user.setEmail("abc@mp.com" ); userMapper.insert(user); user.getId();
删除 1 2 3 4 5 6 7 8 int delete (@Param(Constants.WRAPPER) Wrapper<T> wrapper) ; int deleteBatchIds (@Param(Constants.COLLECTION) Collection<? extends Serializable> idList) ;int deleteById (Serializable id) ;int deleteByMap (@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap) ;
修改 一、根据id更新
1 2 3 4 5 6 7 User user = new User ();user.setUserId(1 ); user.setAge(29 ); user.updateById(); or Integer rows = userMapper.updateById(user);
二、条件构造器作为参数进行更新
1 2 3 4 5 6 7 UpdateWrapper<User> updateWrapper = new UpdateWrapper <>(); updateWrapper.eq("name" ,"shimin" ); User user = new User ();user.setAge(18 ); Integer rows = userMapper.update(user, updateWrapper);
三、条件构造器Set方法
假设只更新一个字段在使用updateWrapper 的构造器中也需要构造一个实体对象,这样比较麻烦。可以使用updateWrapper的set方法
1 2 3 4 5 6 7 UpdateWrapper<User> updateWrapper = new UpdateWrapper <>(); updateWrapper.eq("name" ,"shimin" ).set("age" , 35 ); Integer rows = userMapper.update(null , updateWrapper);sysUserRoleSpecialMapper.update(null , new LambdaUpdateWrapper <SysUserRoleSpecial>().set(SysUserRoleSpecial::getUserId, userIdPortal).eq(SysUserRoleSpecial::getUserId, userId));
lambda构造器
LambdaUpdateWrapper
1 2 3 4 LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper <>(); lambdaUpdateWrapper.eq(User::getRealName, "shimin" ).set(User::getAge, 34 ); Integer rows = userMapper.update(null , lambdaUpdateWrapper);
LambdaUpdateChainWrapper
1 2 3 4 5 6 7 8 9 10 11 LambdaUpdateChainWrapper<User> lambdaUpdateChainWrapper = new LambdaUpdateChainWrapper <>(userMapper); boolean update = lambdaUpdateChainWrapper.eq(User::getRealName, "shimin" ).set(User::getAge, 33 ).update(); lambdaUpdateChainWrapper.eq(User::getRealName, "shimin" ); if (user.getAge == 33 ) { lambdaUpdateChainWrapper.set(User::getAge, 33 ); } lambdaUpdateChainWrapper.update();
查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 T selectById (Serializable id) ; T selectOne (@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ; List<T> selectBatchIds (@Param(Constants.COLLECTION) Collection<? extends Serializable> idList) ; List<T> selectList (@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ; List<T> selectByMap (@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap) ; List<Map<String, Object>> selectMaps (@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ; List<Object> selectObjs (@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ; IPage<T> selectPage (IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ; Page<User> page = new Page <>(1 , 5 ); page.addOrder(OrderItem.asc("age" )); Page<User> userIPage = mapper.selectPage(page, Wrappers.<User>lambdaQuery().eq(User::getAge, 20 ).like(User::getName, "Jack" )); log.error("记录 -------------> {}" , userIPage.getRecords()); log.error("总条数 -------------> {}" , userIPage.getTotal()); IPage<Map<String, Object>> selectMapsPage (IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ; Integer selectCount (@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ;
条件构造器 https://mp.baomidou.com/guide/wrapper.html#abstractwrapper
以下出现的第一个入参 boolean condition
表示该条件是否 加入最后生成的 sql 中,例如:query.like (StringUtils.isNotBlank (name), Entity::getName, name) .eq (age!=null && age >= 0, Entity::getAge, age)
以下代码块内的多个方法均为从上往下补全个别 boolean
类型的入参,默认为 true
以下出现的泛型 Param
均为 Wrapper
的子类实例 (均具有 AbstractWrapper
的所有方法)
以下方法在入参中出现的 R
为泛型,在普通 wrapper 中是 String
, 在 LambdaWrapper 中是函数 (例:Entity::getId
,Entity
为实体类,getId
为字段 id
的 getMethod )
以下方法入参中的 R column
均表示数据库字段,当 R
具体类型为 String
时则为数据库字段名 (字段名是数据库关键字的自己用转义符包裹! )! 而不是实体类数据字段名!!!,另当 R
具体类型为 SFunction
时项目 runtime 不支持 eclipse 自家的编译器!!!
以下举例均为使用普通 wrapper, 入参为 Map
和 List
的均以 json
形式表现!
使用中如果入参的 Map
或者 List
为空 , 则不会加入最后生成的 sql 中!!!
AbstractWrapper 说明:
QueryWrapper (LambdaQueryWrapper) 和 UpdateWrapper (LambdaUpdateWrapper) 的父类 用于生成 sql 的 where 条件,entity 属性也用于生成 sql 的 where 条件 注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
QueryWrapper 说明:
继承自 AbstractWrapper , 自身的内部属性 entity 也用于生成 where 条件 及 LambdaQueryWrapper, 可以通过 new QueryWrapper ().lambda () 方法获取
last - 无视优化规则直接拼接到 sql 的最后
select - 设置查询返回字段 1 2 3 4 5 6 select(String... sqlSelect) select(Predicate<TableFieldInfo> predicate) select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) 例: select("id" , "name" , "age" ) 或 Wrappers.<User>lambdaQuery().select(User::getId) 例: select(i -> i.getProperty().startsWith("test" ))
循环构建条件 1 2 3 4 5 queryWrapper.and(wrapper -> { for (String manageOrgCode: manageOrgCodes){ wrapper.or().likeRight(sys_org_code_name, manageOrgCode); } });
UpdateWrapper 说明:
继承自 AbstractWrapper
, 自身的内部属性 entity
也用于生成 where 条件 及 LambdaUpdateWrapper
, 可以通过 new UpdateWrapper().lambda()
方法获取!
set 1 2 set(String column, Object val) set(boolean condition, String column, Object val)
SQL SET 字段
例: set("name", "老李头")
例: set("name", "")
—> 数据库字段值变为空字符串
例: set("name", null)
—> 数据库字段值变为 null
setSql
设置 SET 部分 SQL
例: `setSql(“name = ‘老李头’”)
lambda()
获取 LambdaWrapper
在 QueryWrapper
中是获取 LambdaQueryWrapper
在 UpdateWrapper
中是获取 `LambdaUpdateWrapper
select 返回指定字段 返回指定字段 1 2 3 4 QueryWrapper<User> queryWrapper = new QueryWrapper <>(); queryWrapper.select("name" , "age" ).like("name" , "雨" ); List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println);
排除指定字段 1 2 3 4 5 QueryWrapper<User> queryWrapper = new QueryWrapper <>(); queryWrapper.select(User.class, info -> !info.getColumn().equals("manager_id" ) && !info.getColumn().equals("create_time" )); List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println);
动态指定字段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LambdaQueryWrapper<SysDepart> query = new LambdaQueryWrapper (); List<SFunction<SysDepart, Object>> columns = new ArrayList <>(); columns.add(SysDepart::getId); columns.add(SysDepart::getDepartName); columns.add(SysDepart::getParentId); columns.add(SysDepart::getOrgCode); columns.add(SysDepart::getDeptLevel); columns.add(SysDepart::getOrgType); if (TableExtendFieldsConfig.tableFieldExist("sys_depart:departLogoType" )) { columns.add(SysDepart::getDepartLogoType); } query.select(columns.toArray(new SFunction []{})); list = this .list(query);
UpdateWrapper 字段设置null 1 2 3 4 5 LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper <>(); wrapper.set(User::getGender(), null ); wrapper.eq(User::getId(), 1 ); userService.update(wrapper);
apply
拼接 sql 条件
1 2 3 4 5 6 7 queryWrapper.lambda().apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'" )--->date_format(dateColumn,'%Y-%m-%d' ) = '2008-08-08' ") //SELECT name, belts FROM divisions WHERE FIND_IN_SET('red', belts); //更多请阅读:https://www.yiibai.com/mysql/find_in_set.html queryWrapper.lambda().apply(" FIND_IN_SET('" + projectCode + "' ,project_code)"); queryWrapper.lambda().apply(sysUser.getOrgCode() + " like concat (sys_org_code, '%' ) ");
last
反向模糊查询 1 queryWrapper.lambda().apply(orgCode + " like concat(org_code, '%')" )
exists 1 2 3 QueryWrapper<AClass> wrapper = new QueryWrapper<>(); wrapper.exists("SELECT 1 FROM B WHERE A.ID = B.AID"); List<AClass> list = this.mapper.selectList(wrapper);
notExists 1 2 3 QueryWrapper<AClass> wrapper = new QueryWrapper <>(); wrapper.notExists("SELECT 1 FROM B WHERE A.ID = B.AID" ); List<AClass> list = this .mapper.selectList(wrapper);
inSql 1 queryWrapper.lambda().inSql(SysPermission::getId, "select permission_id from sys_depart_permission where depart_id='" +departId+"'" );
notInSql 1 querySaWrapper.notInSql(SysAnnouncement::getId, "select annt_id from sys_announcement_send where user_id='" +userId+"'" );
排序 1 2 3 4 5 6 queryWrapper.lambda() .orderByDesc(Event::getMajor) .orderByDesc(Event::getEmergency) .orderByDesc(Event::getStarttime);
复杂的 and or 语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 .eq("a" , "A" ).eq("b" ,B); .eq("a" , "A" ).or().eq("b" ,B); .eq("a" , "A" ).or(i -> i.eq("b" , "B" ).eq("c" , "C" )); .eq("a" , "A" ).or(i -> i.eq("b" , "B" ).or().eq("c" , "C" )); .eq("a" , "A" ).and(i -> i.eq("b" , "B" ).eq("c" , "C" )); .eq("a" , "A" ).and(i -> i.eq("b" , "B" ).or().eq("c" , "C" )); queryWrapper.lambda().and(i -> i.in(Event::getProcInstId, procInsIds) .or() .eq(Event::getCreateBy, sysUser.getUsername())); queryWrapper.lambda().and(i -> i.in(Event::getProcInstId, procInsIds) .or(j -> j.eq(Event::getCreateBy, user.getUsername()).ne(Event::getSourcecode, ActConstant.SOURCECODE_FROM_SCM))); LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper <SysDepart>(); queryWrapper.and(wrapper -> { for (String orgCode: orgCodes){ wrapper.or().likeRight(SysDepart::getOrgCode, orgCode); } });
分页数据-手动组装 1 2 3 4 Page<Crewinfo> crewinfoPage = new Page <Crewinfo>(pageNo, pageSize); List<Crewinfo> crewinfoList = new ArrayList <>(); crewinfoPage.setTotal(userPage.getTotal()); crewinfoPage.setRecords(crewinfoList);
空分页数据 1 2 3 4 5 6 7 8 9 Result<IPage<SysUser>> result = new Result <IPage<SysUser>>(); result.setSuccess(true ); result.setResult(null ); return result;return Result.OK(null );
批量插入 方案一、批量插入
数据库连接中增加 &rewriteBatchedStatements=true
service saveBatch
1 actZEventTaskTimeoutDealService.saveBatch(list, 1000 );
方案二、批量插入数据改造-insertBatchSomeColumn
mysql数据库 mp saveBatch 并不是真正的批量插入。改造如下
循环 insert 数据,提示重复数据 解决:循环内先给 id 赋值为 null
1 2 3 4 5 6 7 List<String> names = new ArrayList <String>() {{ add("123" );add("456" ); }}; Event event = new Event ();for (String name : names) { event.setId(null ); event.setIntro(name); eventMapper.insert(event); }
逻辑删除字段 1 2 @TableLogic(value = "0", delval = "1") private Integer deleted;
Service updateById 方法默认更新策略为 NOT_NULL mybatis-plus FieldStrategy 有三种策略:
IGNORED:0 忽略
NOT_NULL:1 非 NULL,默认策略
NOT_EMPTY:2 非空
而默认更新策略是NOT_NULL:非 NULL;即通过接口更新数据时数据为NULL值时将不更新进数据库
SSM 不再引入 MyBatis 以及 MyBatis-Spring
pom.xml
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus</artifactId > <version > 3.2.0</version > </dependency >
spring.xml
替换原来的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="mapperLocations" value ="classpath:mapping/*.xml" /> <property name ="plugins" > <array > <bean class ="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor" > </bean > </array > </property > </bean > <bean class ="org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name ="basePackage" value ="com.bjtcrj.scm.*.persistence.dao" /> </bean >
问题 XXXServiceImpl 添加了 @DS 切换数据源,更新数据方式采用如下 1 2 3 4 5 6 7 8 this .sysUserPortalService.update(new SysUser ().setPassword(passwordEncode).setSalt(salt), new UpdateWrapper <SysUser>().lambda().eq(SysUser::getUsername, sysUser.getUsername())); this .sysUserPortalService.update(new LambdaUpdateWrapper <SysUser>() .eq(SysUser::getUsername, username) .set(SysUser::getPassword, user.getPassword()) .set(SysUser::getSalt, user.getSalt()));
而不要采用如下:
1 2 3 this .sysUserPortalService.lambdaUpdate() .set(SysUser::getPassword, passwordEncode).set(SysUser::getSalt, salt) .eq(SysUser::getId, id).update();
登录时sysUserService.getOne(queryWrapper)方法报错 1 是由Mybatis-Plus:3.3.2包中的LambdaQueryWrapper导致,用QueryWrapper替换掉LambdaQueryWrapper后正常,后升级mybatis-plus至3.4.0版本,LambdaQueryWrapper问题得到解决,建议将Mybatis-Plus升级!!!
引入 mybatis-plus 后导致日期类型判断错误 Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String1 2 3 <if test ="signin.signtime != null and signin.signtime != ''" > and to_date(to_char(#{signin.signtime},'yyyy-mm-dd'),'yyyy-mm-dd')=to_date(to_char(s.signtime,'yyyy-mm-dd'),'yyyy-mm-dd') </if >
去掉 != ''
判断
1 2 3 <if test ="signin.signtime != null" > and to_date(to_char(#{signin.signtime},'yyyy-mm-dd'),'yyyy-mm-dd')=to_date(to_char(s.signtime,'yyyy-mm-dd'),'yyyy-mm-dd') </if >
非数据库字段 1 2 @TableField(exist=false) private String other;