MyBatis-Plus中update修改成功,但实际数据并未被修改的解决办法

本文详细解析了使用MyBatis-Plus API在动态修改数据时遇到的问题及解决办法,尤其针对实体字段不全传参时,如何使未传参数字段更新为空值的场景,介绍了字段自动填充与验证策略的应用。

前言

关于这篇文章,主要针对通过调用 MyBatis-Plus API 实现动态修改数据时遇到的,不是使用的SQL,如果你使用的是SQL则建议移步到相关文章,以免耽误你的工作时间,如果是SQL,在移步前博主给两个关注点可以尝试去解决:一个是事务方面,有可能数据已经修改成功,但是在下一步的操作中,可能在某步失败后导致全局数据回滚,所以修改失败;二一个是设置自动提交openSession( autoCommit=true),在CRUD的业务逻辑代码后加上 sqlSession.commit() 即可。

再次声明,本篇博文主要针对 MyBatis-Plus API 调用跟新时,失败的解决办法,下面很长。

 

问题概述

当实体的所有字段都不为空时,修改是成功的,但是,当某个字段的值不传时,修改成功后,该字段还是显示原来的值。目的,希望此时该字段的实际数据被修改为空,但实际数据未作任何改变。

关于这个问题,网上没有可行的解决办法,只能将问题结合源码进行分析并找到合适的解决方案,下面是博主的源码分析,不到之处望指正。

 

问题前奏(示例性代码,目的为阐述问题):

1、接收数据

package com.travel.etcp.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 
 * @author HuaZai
 * @contact who.seek.me@java98k.vip
 *          <ul>
 * @description
 *              <li>一号服务器
 *              </ul>
 * @className EmployeeController
 * @package com.travel.etcp.controller
 * @createdTime 2018年05月05日 下午5:52:07
 *
 * @version V1.0.0
 */
@Slf4j
@RestController
@Api(value = "/emp")
@RequestMapping("/emp")
public class EmployeeController
{
	@Autowired
	public EmployeeService employeeService;

	@SysLog("用户模块-修改用户")
	@PutMapping("/updateEmployeeById")
	@ApiOperation(value = "/updateEmployeeById", notes = "通过EmployeeId更新用户信息")
	public ResponseData updateEmployeeById(
			@ApiParam(name = "employee", value = "Employee 实体") @RequestParam(name = "employee") Employeee employee)
	{
		try
		{
			String header = HttpContextUtils.getHttpServletRequest().getHeader(Constant.TERMINAL_TYPE_HEADER);
			employeeService.updateEmployeeById(employee);
		} catch (Exception e)
		{
			log.info(Constant.INFO_LOG_MSG, e.getMessage()); // 这个需要配置插件
			return ResponseData.error(RespCodeEnum.ERROR_INTERNAL_SERVER.getCode(), "用户修改失败");
		}
		return ResponseData.ok();
	}
}

2、服务接口:

package com.travel.etcp.service;

import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.IService;

import java.util.List;

/**
 * 
 * @author HuaZai
 * @contact who.seek.me@java98k.vip
 *          <ul>
 * @description
 *              <li>TODO
 *              </ul>
 * @className EmployeeService
 * @package com.travel.etcp.service
 * @createdTime 2018年05月05日 下午4:32:07
 *
 * @version V1.0.0
 */
public interface EmployeeService extends IService<Employee>
{

	void updateEmployeeById(Employee employee);

}

 

3、 数据处理:

package com.travel.etcp.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 
 * @author HuaZai
 * @contact who.seek.me@java98k.vip
 *          <ul>
 * @description
 *              <li>TODO
 *              </ul>
 * @className EmployeeServiceImpl
 * @package com.travel.etcp.service.impl
 * @createdTime 2018年05月05日 下午4:58:13
 *
 * @version V1.0.0
 */
@Slf4j
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService
{

	@Autowired
	private EmployeeService employeeService;

	@Autowired
	private EmployeeMapper employeeMapper;

	@Transactional
	@Override
	public void updateEmployeeById(Employee employee)
	{
		try
		{
			// 这儿因为继承了MyBatis的ServiceImpl类,而在ServiceImpl中实现了IService类,
			// 所以这儿可以去掉employeeService 而直接调用API:updateById(employee);
			employeeService.updateById(employee);

		} catch (Exception e)
		{
			log.error(Constant.INFO_LOG_MSG, e.getMessage());
		}
	}
}

 

源码解析:

1、以直接调用 API 的方式,所以从 ServiceImpl 类开始,如下:

package com.baomidou.mybatisplus.service.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T>
{
	@Autowired
	protected M baseMapper;

	public ServiceImpl()
	{
	}

	protected static boolean retBool(Integer result)
	{
		return SqlHelper.retBool(result);
	}

	protected Class<T> currentModelClass()
	{
		return ReflectionKit.getSuperClassGenricType(this.getClass(), 1);
	}

	protected SqlSession sqlSessionBatch()
	{
		return SqlHelper.sqlSessionBatch(this.currentModelClass());
	}

	protected String sqlStatement(SqlMethod sqlMethod)
	{
		return SqlHelper.table(this.currentModelClass()).getSqlStatement(sqlMethod.getMethod());
	}

	/**
	 * 省略部分代码
	 */

	@Transactional(rollbackFor =
	{ Exception.class })
	public boolean updateById(T entity)
	{
		return retBool(this.baseMapper.updateById(entity));
	}

	/**
	 * 省略部分代码
	 */
}

 

2、查看API接口 IService 类 ,如下:

package com.baomidou.mybatisplus.service;

import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public interface IService<T>
{
	boolean insert(T var1);

	boolean insertAllColumn(T var1);

	boolean insertBatch(List<T> var1);

	boolean insertBatch(List<T> var1, int var2);

	boolean insertOrUpdateBatch(List<T> var1);

	boolean insertOrUpdateBatch(List<T> var1, int var2);

	boolean insertOrUpdateAllColumnBatch(List<T> var1);

	boolean insertOrUpdateAllColumnBatch(List<T> var1, int var2);

	boolean deleteById(Serializable var1);

	boolean deleteByMap(Map<String, Object> var1);

	boolean delete(Wrapper<T> var1);

	boolean deleteBatchIds(Collection<? extends Serializable> var1);

	boolean updateById(T var1);

	boolean updateAllColumnById(T var1);

	boolean update(T var1, Wrapper<T> var2);

	boolean updateBatchById(List<T> var1);

	boolean updateBatchById(List<T> var1, int var2);

	boolean updateAllColumnBatchById(List<T> var1);

	boolean updateAllColumnBatchById(List<T> var1, int var2);

	boolean insertOrUpdate(T var1);

	boolean insertOrUpdateAllColumn(T var1);

	T selectById(Serializable var1);

	List<T> selectBatchIds(Collection<? extends Serializable> var1);

	List<T> selectByMap(Map<String, Object> var1);

	T selectOne(Wrapper<T> var1);

	Map<String, Object> selectMap(Wrapper<T> var1);

	Object selectObj(Wrapper<T> var1);

	int selectCount(Wrapper<T> var1);

	List<T> selectList(Wrapper<T> var1);

	Page<T> selectPage(Page<T> var1);

	List<Map<String, Object>> selectMaps(Wrapper<T> var1);

	List<Object> selectObjs(Wrapper<T> var1);

	Page<Map<String, Object>> selectMapsPage(Page var1, Wrapper<T> var2);

	Page<T> selectPage(Page<T> var1, Wrapper<T> var2);
}

如下图:

 

3、@TableField 源码如下

package com.baomidou.mybatisplus.annotation;

import java.lang.annotation.*;

/**
 * 表字段标识
 *
 * @author hubin sjy tantan
 * @since 2016-09-09
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TableField
{

	/**
	 * 字段值(驼峰命名方式,该值可无)
	 */
	String value() default "";

	/**
	 * 当该Field为类对象时, 可使用#{对象.属性}来映射到数据表.
	 * <p>
	 * 支持:@TableField(el = "role, jdbcType=BIGINT)
	 * </p>
	 * <p>
	 * 支持:@TableField(el = "role,
	 * typeHandler=com.baomidou.springcloud.typehandler.PhoneTypeHandler")
	 * </p>
	 */
	String el() default "";

	/**
	 * 是否为数据库表字段
	 * <p>
	 * 默认 true 存在,false 不存在
	 * </p>
	 */
	boolean exist() default true;

	/**
	 * 字段 where 实体查询比较条件
	 * <p>
	 * 默认 `=` 等值
	 * </p>
	 */
	String condition() default "";

	/**
	 * 字段 update set 部分注入, 该注解优于 el 注解使用
	 * <p>
	 * 例如:@TableField(.. , update="%s+1") 其中 %s 会填充为字段
	 * </p>
	 * <p>
	 * 输出 SQL 为:update 表 set 字段=字段+1 where ...
	 * </p>
	 * <p>
	 * 例如:@TableField(.. , update="now()") 使用数据库时间
	 * </p>
	 * <p>
	 * 输出 SQL 为:update 表 set 字段=now() where ...
	 * </p>
	 */
	String update() default "";

	/**
	 * 字段验证策略
	 * <p>
	 * 默认追随全局配置
	 * </p>
	 */
	FieldStrategy strategy() default FieldStrategy.DEFAULT;

	/**
	 * 字段自动填充策略
	 */
	FieldFill fill() default FieldFill.DEFAULT;

	/**
	 * 是否进行 select 查询
	 * <p>
	 * 大字段可设置为 false 不加入 select 查询范围
	 * </p>
	 */
	boolean select() default true;

}

 

在上面的源码的解析中,已知 MyBatis-Plus 已经能满足所有的一般业务需求,如果对MyBatis-Plus不熟悉,或者知其然而不知其所以然,则会处处碰壁,MyBatis-Plus 的核心思想或者目的就是 “ 为简化开发而生 ”,官方宣言如下图:

 

 

解决办法

 

该问题的解决办法分为两步:

  1. 启动字段的自动填充功能;
  2. 开启字段的验证策略,能根据 Entity 产出 SQL 的字段验证策略,这个就有点类似于SQL里面的if标签;
  3. 在实体类的主建ID上新增“@Id”的注解,注意导包:import javax.persistence.Id;

 

所以API调用的通过以上两步即可轻松解决,如果是SQL则还需要移步到相关文章了。

 

 

 

参考MyBatis-Plus源码:MyBatis-Plus源码地址

MyBatis-Plus文档:MyBatis-Plus文档地址


好了,关于 MyBatis-Plus中update修改成功,但实际数据并未被修改的解决办法 就写到这儿了,如果还有什么疑问或遇到什么问题欢迎扫码提问,也可以给我留言哦,我会一一详细的解答的。 
歇后语:“ 共同学习,共同进步 ”,也希望大家多多关注CSND的IT社区。


作       者:华    仔
联系作者:who.seek.me@java98k.vip
来        源:优快云 (Chinese Software Developer Network)
原        文:https://blog.youkuaiyun.com/Hello_World_QWP/article/details/88056976
版权声明:本文为博主原创文章,请在转载时务必注明博文出处!
<think>好的,我现在遇到了MyBatis-Plus的问题,提示“ONLY SUPPORT SAME TYPE (UPDATE OR DELETE) MULTI SQL”。用户需要解决这个问题,但不太清楚具体情况。首先,我需要回忆一下MyBatis-Plus的相关知识,特别是关于批量操作的部分。 MyBatis-Plus在批量操作时,比如批量更新或删除,通常使用`updateBatch`或`deleteBatch`方法。但根据错误信息来看,可能是因为在一个批量操作中混合了不同类型的SQL语句,比如同时有更新和删除,而MyBatis-Plus不支持这样的混合操作。这个错误提示的意思应该是,在一个批量操作中,所有的SQL语句必须是同一类型,要么全是更新,要么全是删除,不能混用。 接下来,我需要验证这个猜测是否正确。比如,用户可能在代码中尝试同时执行多个不同类型的SQL语句,例如在一个批处理中既有update也有delete语句。这种情况下,MyBatis-Plus会抛出这个错误。那么解决方案应该是将这些操作拆分成两个不同的批处理,分别执行更新和删除。 此外,可能还需要检查用户是否正确地使用了MyBatis-Plus的批量操作方法。比如,是否使用了`SqlHelper`或者`SqlMethod`中的正确方法,或者在配置中是否正确启用了批处理功能。例如,需要在配置文件中设置`mybatis-plus.global-config.db-config.logic-delete-field`等属性,但这里可能不相关,因为问题在于操作类型不一致。 然后,考虑可能的解决方案。首先,拆分不同的操作类型到不同的批处理中。如果用户确实需要在一个事务中执行多个不同类型的操作,可能需要手动管理事务,或者使用MyBatis的原生批处理功能,通过编写自定义的Mapper XML文件,将多个操作放在一个事务中执行。但MyBatis-Plus的批量方法可能不支持混合类型,所以需要换用其他方法。 另外,可以建议用户检查是否有第三方插件或配置导致了这个问题,比如分页插件或者其他拦截器可能影响批处理的执行。或者,是否在同一个方法中错误地混用了不同的操作类型,比如在循环中同时添加了update和delete的语句到同一个批处理中。 还需要考虑MyBatis-Plus的版本问题。某些旧版本可能存在限制,而新版本可能已经支持,或者有其他的处理方式。建议用户检查版本,并查阅官方文档或社区讨论,看看是否有相关的解决方案或已知的问题。 最后,生成相关问题的时候,需要围绕MyBatis-Plus的批量操作、事务管理、配置优化等方面展开,帮助用户进一步理解和解决类似的问题。</think>在MyBatis-Plus中遇到`ONLY SUPPORT SAME TYPE (UPDATE OR DELETE) MULTI SQL`错误时,通常是由于批量操作中混合了不同类型的SQL语句(如同时包含更新和删除)。以下是解决方案: ### 解决方案 1. **拆分操作类型** 将更新和删除操作分别封装为独立的批量操作: ```java // 错误示例:混合类型操作 List<SQL> sqlList = Arrays.asList(updateSql, deleteSql); // 正确做法:分离为两个操作 List<SQL> updateList = Arrays.asList(updateSql1, updateSql2); List<SQL> deleteList = Arrays.asList(deleteSql1, deleteSql2); mybatisMapper.executeBatch(updateList); mybatisMapper.executeBatch(deleteList); ``` 2. **使用原生MyBatis批处理** 通过自定义Mapper XML实现混合操作: ```xml <!-- Mapper.xml --> <update id="batchMixedOperation"> <foreach collection="list" item="sql" separator=";"> ${sql} </foreach> </update> ``` ```java // 调用时需手动控制事务 @Transactional public void executeMixedSql(List<String> sqlList) { myMapper.batchMixedOperation(sqlList); } ``` 3. **检查全局配置** 确认`application.yml`中限制批量操作类型: ```yaml mybatis-plus: global-config: db-config: logic-delete-field: deleted # 逻辑删除字段(若涉及) ``` ### 原理说明 MyBatis-Plus的批量操作底层通过`SqlSession`的`executeBatch`实现,该方法要求同一批次中的SQL语句必须为同一类型(全更新或全删除)以优化执行效率[^1]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechBro华仔

日拱一卒无有尽,功不唐捐终入海

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值