baseMapper.updateById null属性未更新到数据库

本文介绍MyBatis-Plus中如何处理实体类字段为null的情况,包括全局配置field-strategy、针对特定字段设置策略及使用UpdateWrapper进行更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis-plus 的默认配置 在调用 baseMapper.updateById 时 ,如果实体类中的字段属性为null,那么不将该属性更新到数据库。

mybatis-plus FieldStrategy 有三种策略:

IGNORED:0 忽略
NOT_NULL:1 非 NULL,默认策略
NOT_EMPTY:2 非空
而默认更新策略是NOT_NULL:非 NULL;即通过接口更新数据时数据为NULL值时将不更新进数据库。

如果想将查询结果中某个字段原本不为null的值更新为null(数据库设计允许为null),解决办法如下:

  1. 设置全局的field-strategy
    在配置文件中,我们可以修改策略,如下:
#properties文件格式:
mybatis-plus.global-config.db-config.field-strategy=ignored

#yml文件格式:
mybatis-plus:
  global-config:
  	#字段策略 0:"忽略判断",1:"非 NULL 判断",2:"非空判断"
    field-strategy: 0

这样做是全局性配置,会对所有的字段都忽略判断,如果一些字段不想要修改,但是传值的时候没有传递过来,就会被更新为null,可能会影响其他业务数据的正确性
2. 对某个字段设置单独的field-strategy

根据具体情况,在需要更新的字段中调整验证注解,如验证非空:
@TableField(strategy=FieldStrategy.NOT_EMPTY)

这样的话,我们只需要在需要更新为null的字段上,设置忽略策略,如下:

/**
 * 购买时间
 */
@TableField(strategy = FieldStrategy.IGNORED)
private Date buyTime;

使用上述方法,如果需要这样处理的字段较多,那么就需要涉及对各个字段上都添加该注解,显得有些麻烦了。

那么,可以考虑使用第三种方法,不需要在字段上加注解也能更新成功。

  1. 使用UpdateWrapper方式更新

在mybatis-plus中,除了updateById方法,还提供了一个update方法,直接使用update方法也可以将字段设置为null,代码如下:

 /**
  * update更新字段为null
  * @param id
  * @return
  */
 @Override
 public boolean updateArticleById(Integer id) {
     Article article = Optional.ofNullable(articleMapper.selectById(id)).orElseThrow(RuntimeException::new);
     LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<>();
     updateWrapper.set(Article::getOfflineTime,null);
     updateWrapper.set(Article::getContent,"try mybatis plus update null");
     updateWrapper.set(Article::getPublishTime,LocalDateTime.now().plusHours(8));
     updateWrapper.eq(Article::getId,article.getId());
     int i = articleMapper.update(article, updateWrapper);
     return i==1;
 }

@Resource private DzhlConfig dzhlConfig; @Resource private SysDownloadChunksService chunksService; @Resource private WGConfig wgConfig; /** * 任务ID到Future的映射,用于控制任务 */ private final ConcurrentHashMap<Long, Future<?>> taskFutures = new ConcurrentHashMap<>(); /** * 下载任务线程池 */ private final ExecutorService downloadExecutor = Executors.newFixedThreadPool(5); /** * 分片下载线程池 */ private final ExecutorService chunkExecutor = Executors.newFixedThreadPool(5 * 3); // 在服务类中添加HttpClient成员变量 private CloseableHttpClient httpClient; // 在服务类初始化时创建HttpClient @PostConstruct public void init() { this.httpClient = createHttpClient(); } // 在服务类销毁时关闭HttpClient @PreDestroy public void destroy() { try { if (httpClient != null) { httpClient.close(); } } catch (IOException e) { log.error("关闭HTTP客户端失败", e); } } @Override @Transactional public Long createTask(String url, String onlyName) throws IOException { // 创建任务对象 SysDownloadTasks task = new SysDownloadTasks(); task.setFileUrl(url); task.setSavePath(DzhlConfig.getUploadPath() + "/" + DateUtils.dateTime() + "/" + onlyName); task.setOnlyName(onlyName); task.setStatus(SysDownloadTasks.DownloadStatus.PENDING); task.setDownloadedSize(0L); log.info("创建下载任务: url={}, savePath={}", url, task.getSavePath()); // 解析文件名 URL fileUrl = new URL(url); String path = fileUrl.getPath(); task.setFileName(path.substring(path.lastIndexOf('/') + 1)); // 获取文件大小并初始化分片 initializeChunks(task, dzhlConfig.getDefaultChunkSize()); // 保存任务到数据库 baseMapper.insert(task); log.info("下载任务创建成功: taskId={}", task.getId()); return task.getId(); } /** * 初始化文件分片 * * @param task 下载任务 * @param chunkSize 分片大小 */ private void initializeChunks(SysDownloadTasks task, long chunkSize) throws IOException { log.info("初始化文件分片: taskId={}, chunkSize={}", task.getId(), chunkSize); HttpResponse httpResponse = casterLogin(); JSONObject loginResultObj = JSONObject.parseObject(EntityUtils.toString(httpResponse.getEntity(), "UTF-8")); if (httpResponse.getStatusLine().getStatusCode() == 200) { // 创建HTTP连接获取文件大小 HttpURLConnection connection = null; try { URL url = new URL(task.getFileUrl()); connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("Authorization", loginResultObj.get("token").toString()); connection.setRequestMethod("HEAD"); int responseCode = connection.getResponseCode(); if (responseCode != 200) { throw new IOException("获取文件信息失败,HTTP状态码: " + responseCode); } // 获取文件大小 long fileSize = connection.getContentLengthLong(); if (fileSize <= 0) { throw new IOException("无法获取文件大小"); } task.setTotalSize(fileSize); // 计算分片数量 int chunkCount = (int) Math.ceil((double) fileSize / chunkSize); task.setChunkCount(chunkCount); // 创建分片信息 List<SysDownloadChunks> chunks = new ArrayList<>(); for (int i = 0; i < chunkCount; i++) { long startByte = i * chunkSize; long endByte = (i == chunkCount - 1) ? fileSize - 1 : (i + 1) * chunkSize - 1; SysDownloadChunks chunk = new SysDownloadChunks(); chunk.setTaskId(task.getId()); chunk.setChunkIndex(i); chunk.setStartByte(startByte); chunk.setEndByte(endByte); chunk.setDownloadedBytes(0L); chunk.setTempLocation(dzhlConfig.getTempLocation() + DateUtils.dateTime() + task.getOnlyName() + "/chunk_" + i); chunk.setStatus(SysDownloadChunks.ChunkStatus.PENDING); chunk.setRetryCount(0); chunks.add(chunk); } // 保存分片信息到数据库 chunksService.saveBatch(chunks); log.info("文件分片初始化完成: taskId={}, 分片数量={}", task.getId(), chunkCount); } finally { if (connection != null) { connection.disconnect(); } } // } } log.info("导播机登录失败!"); } @Override @Async public void startTask(Long taskId) { log.info("开始下载任务: taskId={}", taskId); // 获取任务信息 SysDownloadTasks task = baseMapper.selectById(taskId); System.out.println("aaaa" + task); System.out.println("eeeee" + task.getStatus()); if (task == null) { log.error("下载任务不存在: taskId={}", taskId); return; } // 检查任务状态 if (!"PENDING".equals(task.getStatus().toString()) && !"PAUSED".equals(task.getStatus().toString())) { log.info("任务状态不是待下载或已暂停,无法开始: taskId={}, status={}", taskId, task.getStatus()); return; } // 更新任务状态 task.setStatus(SysDownloadTasks.DownloadStatus.DOWNLOADING); task.setCreateTime(new Date()); baseMapper.updateById(task); // 提交任务到线程池 Future<?> future = downloadExecutor.submit(() -> downloadFile(task)); taskFutures.put(taskId, future); log.info("下载任务已提交执行: taskId={}", taskId); } /** * 下载文件主方法 * * @param task 下载任务 */ private void downloadFile(SysDownloadTasks task) { log.info("开始下载文件: taskId={}, fileName={}", task.getId(), task.getFileName()); try { // 获取所有分片 List<SysDownloadChunks> chunks = chunksService.list(new LambdaQueryWrapper<SysDownloadChunks>() .eq(SysDownloadChunks::getTaskId, task.getId())); if (chunks == null || chunks.isEmpty()) { log.error("没有找到分片信息: taskId={}", task.getId()); task.setStatus(SysDownloadTasks.DownloadStatus.FAILED); task.setErrorMessage("没有找到分片信息"); baseMapper.updateById(task); return; } // 并行下载所有分片 List<Future<Boolean>> futures = new ArrayList<>(); for (SysDownloadChunks chunk : chunks) { if (!"COMPLETED".equals(chunk.getStatus().toString())) { SysDownloadTasks finalTask = task; futures.add(chunkExecutor.submit(() -> downloadChunk(finalTask, chunk))); } } // 等待所有分片下载完成 boolean allSuccess = true; for (Future<Boolean> future : futures) { if (!future.get()) { allSuccess = false; break; } } // 检查任务状态 task = baseMapper.selectById(task.getId()); if (!allSuccess || !"DOWNLOADING".equals(task.getStatus().toString())) { log.info("下载任务已暂停或取消: taskId={}", task.getId()); return; } // 合并所有分片 if (mergeChunks(task)) { // 计算MD5校验和 String md5 = calculateMD5(task.getSavePath()); task.setOnlyName(md5); // 清理临时文件 cleanupTempFiles(task); // 更新任务状态为已完成 task.setStatus(SysDownloadTasks.DownloadStatus.COMPLETED); task.setUpdateTime(new Date()); baseMapper.updateById(task); log.info("文件下载完成: taskId={}, fileName={}, MD5={}", task.getId(), task.getFileName(), md5); } else { task.setStatus(SysDownloadTasks.DownloadStatus.FAILED); task.setErrorMessage("合并分片失败"); baseMapper.updateById(task); log.error("合并分片失败: taskId={}", task.getId()); } } catch (Exception e) { log.error("下载文件异常: taskId={}", task.getId(), e); task.setStatus(SysDownloadTasks.DownloadStatus.FAILED); task.setErrorMessage("下载过程中发生异常: " + e.getMessage()); baseMapper.updateById(task); } finally { // 从任务映射中移除 taskFutures.remove(task.getId()); } } /** * 下载单个分片 * * @param task 下载任务 * @param chunk 分片信息 * @return 下载是否成功 */ private boolean downloadChunk(SysDownloadTasks task, SysDownloadChunks chunk) { log.info("开始下载分片: taskId={}, chunkIndex={}", task.getId(), chunk.getChunkIndex()); // 更新分片状态 chunk.setStatus(SysDownloadChunks.ChunkStatus.DOWNLOADING); chunksService.updateById(chunk); // 检查任务状态 task = baseMapper.selectById(task.getId()); if (!"DOWNLOADING".equals(task.getStatus().toString())) { log.info("任务状态不是下载中,停止下载分片: taskId={}, chunkIndex={}", task.getId(), chunk.getChunkIndex()); chunk.setStatus(SysDownloadChunks.ChunkStatus.PENDING); chunksService.updateById(chunk); return false; } //封装导播机登录参数 // JSONObject loginJsonObject = new JSONObject(); // loginJsonObject.put("username", wgConfig.getUsername()); // loginJsonObject.put("password", wgConfig.getPassword()); // // // 登录 // HttpRequest login = HttpUtil.createPost(wgConfig.getHost(1L) + "login"); // HttpResponse loginExecute = login.body(loginJsonObject.toJSONString()).execute(); // JSONObject loginResultObj = JSONObject.parseObject(loginExecute.body()); // if (loginResultObj.getInteger("code") == 200) { HttpResponse httpResponse = casterLogin(); JSONObject loginResultObj = null; try { loginResultObj = JSONObject.parseObject(EntityUtils.toString(httpResponse.getEntity(), "UTF-8")); } catch (IOException e) { e.printStackTrace(); throw new ServiceException("系统错误:" + e.getMessage()); } if (httpResponse.getStatusLine().getStatusCode() == 200) { // 创建HTTP连接 HttpGet httpGet = null; // HttpURLConnection connection = null; try { // 创建HTTP请求 httpGet = new HttpGet(task.getFileUrl()); // URL url = new URL(task.getFileUrl()); // connection = (HttpURLConnection) url.openConnection(); httpGet.setHeader("Authorization", loginResultObj.get("token").toString()); // connection.setRequestProperty("Authorization", loginResultObj.get("token").toString()); // connection.setRequestMethod("GET"); // 设置Range头,指定下载范围 long startPos = chunk.getStartByte() + chunk.getEndByte(); httpGet.setHeader("Range", "bytes=" + startPos + "-" + chunk.getEndByte()); // connection.setRequestProperty("Range", "bytes=" + startPos + "-" + chunk.getEndByte()); // 设置超时时间 // connection.setConnectTimeout(5000); // connection.setReadTimeout(dzhlConfig.getReadTimeout()); // 发送请求并获取响应 // int responseCode = connection.getResponseCode(); org.apache.http.HttpResponse response = httpClient.execute(httpGet); int responseCode = response.getStatusLine().getStatusCode(); // 206表示Partial Content if (responseCode != 206) { log.error("下载分片失败,HTTP状态码: {}, taskId={}, chunkIndex={}", responseCode, task.getId(), chunk.getChunkIndex()); // 消耗实体避免连接泄漏 EntityUtils.consumeQuietly(response.getEntity()); return handleChunkDownloadFailure(task, chunk); } // 确保临时文件存在 File tempFile = new File(chunk.getTempLocation()); if (!tempFile.exists()) { tempFile.getParentFile().mkdirs(); tempFile.createNewFile(); } // 打开文件进行追加写入 try (RandomAccessFile out = new RandomAccessFile(tempFile, "rw"); InputStream in = response.getEntity().getContent()) { // 设置文件指针位置 out.seek(chunk.getDownloadedBytes()); // 缓冲区大小8KB byte[] buffer = new byte[8192]; int bytesRead; // 读取数据并写入文件 while ((bytesRead = in.read(buffer)) != -1) { // 检查任务状态 task = baseMapper.selectById(task.getId()); if (!"DOWNLOADING".equals(task.getStatus().toString())) { log.info("任务状态不是下载中,停止下载分片: taskId={}, chunkIndex={}", task.getId(), chunk.getChunkIndex()); return false; } out.write(buffer, 0, bytesRead); // 更新已下载大小 long currentDownloaded = chunk.getDownloadedBytes() + bytesRead; chunk.setDownloadedBytes(currentDownloaded); chunksService.updateById(chunk); // 更新任务已下载大小 System.out.println("$$$$$" + task.getDownloadedSize() + "/" + bytesRead); task.setDownloadedSize(task.getDownloadedSize() + bytesRead); baseMapper.updateById(task); } // 分片下载完成 chunk.setStatus(SysDownloadChunks.ChunkStatus.COMPLETED); chunksService.updateById(chunk); log.info("分片下载完成: taskId={}, chunkIndex={}", task.getId(), chunk.getChunkIndex()); return true; } } catch (Exception e) { log.error("下载分片异常: taskId={}, chunkIndex={}", task.getId(), chunk.getChunkIndex(), e); return handleChunkDownloadFailure(task, chunk); } finally { if (httpGet != null) { httpGet.releaseConnection(); } } // } } log.info("导播机登录失败!"); return false; } /** * 处理分片下载失败 * * @param task 下载任务 * @param chunk 分片信息 * @return 是否需要重试 */ private boolean handleChunkDownloadFailure(SysDownloadTasks task, SysDownloadChunks chunk) { // 增加重试次数 chunk.setRetryCount(chunk.getRetryCount() + 1); chunk.setStatus(SysDownloadChunks.ChunkStatus.FAILED); chunk.setErrormessage("下载失败,尝试重试"); chunksService.updateById(chunk); // 如果重试次数小于配置的最大重试次数,则重试 if (chunk.getRetryCount() < dzhlConfig.getRetryCount()) { log.info("分片下载失败,准备重试: taskId={}, chunkIndex={}, 重试次数={}", task.getId(), chunk.getChunkIndex(), chunk.getRetryCount()); // 等待一段时间后重试 try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } return downloadChunk(task, chunk); } else { // 达到最大重试次数,标记为失败 chunk.setStatus(SysDownloadChunks.ChunkStatus.FAILED); chunk.setErrormessage("达到最大重试次数"); chunksService.updateById(chunk); // 整个任务也标记为失败 task.setStatus(SysDownloadTasks.DownloadStatus.FAILED); task.setErrorMessage("分片 " + chunk.getChunkIndex() + " 下载失败,达到最大重试次数"); baseMapper.updateById(task); log.error("分片下载失败,达到最大重试次数: taskId={}, chunkIndex={}, 重试次数={}", task.getId(), chunk.getChunkIndex(), chunk.getRetryCount()); return false; } } /** * 合并所有分片 * * @param task 下载任务 * @return 合并是否成功 */ private boolean mergeChunks(SysDownloadTasks task) { log.info("开始合并分片: taskId={}", task.getId()); // 确保目标目录存在 File targetFile = new File(task.getSavePath()); File parentDir = targetFile.getParentFile(); if (!parentDir.exists()) { parentDir.mkdirs(); } // 获取所有分片 List<SysDownloadChunks> chunks = chunksService.list(new LambdaQueryWrapper<SysDownloadChunks>() .eq(SysDownloadChunks::getTaskId, task.getId())); if (chunks == null || chunks.isEmpty()) { log.error("没有找到分片信息,无法合并: taskId={}", task.getId()); return false; } // 按分片索引排序 chunks.sort(Comparator.comparing(SysDownloadChunks::getChunkIndex)); try (RandomAccessFile out = new RandomAccessFile(targetFile, "rw")) { // 按顺序合并所有分片 for (SysDownloadChunks chunk : chunks) { try (FileInputStream in = new FileInputStream(chunk.getTempLocation())) { byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } } } log.info("分片合并完成: taskId={}", task.getId()); return true; } catch (IOException e) { log.error("合并分片失败: taskId={}", task.getId(), e); return false; } } /** * 计算文件的MD5校验和 * * @param filePath 文件路径 * @return MD5校验和 */ private String calculateMD5(String filePath) { log.info("计算文件MD5: filePath={}", filePath); try (FileInputStream fis = new FileInputStream(filePath)) { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { md.update(buffer, 0, bytesRead); } byte[] digest = md.digest(); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b & 0xff)); } String md5 = sb.toString(); log.info("MD5计算完成: filePath={}, MD5={}", filePath, md5); return md5; } catch (IOException | NoSuchAlgorithmException e) { log.error("计算MD5失败: filePath={}", filePath, e); return null; } } /** * 清理临时文件 * * @param task 下载任务 */ private void cleanupTempFiles(SysDownloadTasks task) { log.info("清理临时文件: taskId={}", task.getId()); try { // 删除所有分片临时文件 List<SysDownloadChunks> chunks = chunksService.list(new LambdaQueryWrapper<SysDownloadChunks>() .eq(SysDownloadChunks::getTaskId, task.getId())); if (chunks != null) { for (SysDownloadChunks chunk : chunks) { Path path = Paths.get(chunk.getTempLocation()); Files.deleteIfExists(path); } // 删除任务临时目录 Path tempDirPath = Paths.get(chunks.get(0).getTempLocation()); Files.deleteIfExists(tempDirPath); } log.info("临时文件清理完成: taskId={}", task.getId()); } catch (IOException e) { log.error("清理临时文件失败: taskId={}", task.getId(), e); } } @Override public void pauseTask(Long taskId) { log.info("暂停下载任务: taskId={}", taskId); // 获取任务信息 SysDownloadTasks task = baseMapper.selectById(taskId); if (task == null) { log.error("下载任务不存在: taskId={}", taskId); return; } // 检查任务状态 if (!"DOWNLOADING".equals(task.getStatus().toString())) { log.info("任务状态不是下载中,无法暂停: taskId={}, status={}", taskId, task.getStatus()); return; } // 更新任务状态 task.setStatus(SysDownloadTasks.DownloadStatus.PAUSED); baseMapper.updateById(task); // 取消任务执行 Future<?> future = taskFutures.get(taskId); if (future != null && !future.isDone()) { future.cancel(true); } log.info("下载任务已暂停: taskId={}", taskId); } @Override public void resumeTask(Long taskId) { log.info("恢复下载任务: taskId={}", taskId); // 直接调用startTask,它会检查状态并处理恢复逻辑 startTask(taskId); } @Override public void cancelTask(Long taskId) { log.info("取消下载任务: taskId={}", taskId); // 获取任务信息 SysDownloadTasks task = baseMapper.selectById(taskId); if (task == null) { log.error("下载任务不存在: taskId={}", taskId); return; } // 更新任务状态 task.setStatus(SysDownloadTasks.DownloadStatus.CANCELED); baseMapper.updateById(task); // 取消任务执行 Future<?> future = taskFutures.get(taskId); if (future != null && !future.isDone()) { future.cancel(true); } // 清理临时文件 cleanupTempFiles(task); log.info("下载任务已取消: taskId={}", taskId); } @Override public SysDownloadTasks getTask(Long taskId) { return baseMapper.selectById(taskId); } private HttpResponse casterLogin() { try { //封装导播机登录参数 JSONObject loginJsonObject = new JSONObject(); loginJsonObject.put("username", wgConfig.getUsername()); loginJsonObject.put("password", wgConfig.getPassword()); HttpPost httpPost = new HttpPost(wgConfig.getHost(1L) + "login"); // 设置JSON实体到请求中 StringEntity entity = new StringEntity(loginJsonObject.toString(), "UTF-8"); entity.setContentType("application/json"); httpPost.setEntity(entity); // 执行请求 return httpClient.execute(httpPost); } catch (IOException e) { e.printStackTrace(); throw new ServiceException("系统错误:" + e.getMessage()); } } /** * HTTP 客户端创建方法 */ private CloseableHttpClient createHttpClient() { // 创建连接池管理器 PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(dzhlConfig.getHttpMaxTotalConnections()); cm.setDefaultMaxPerRoute(dzhlConfig.getHttpMaxConnectionsPerRoute()); // 创建HTTP客户端,配置连接超时和读取超时 return HttpClients.custom() .setConnectionManager(cm) .setConnectionTimeToLive(dzhlConfig.getHttpConnectionTimeToLive(), TimeUnit.MILLISECONDS) .setDefaultRequestConfig(RequestConfig.custom() .setConnectTimeout((int) dzhlConfig.getConnectTimeout()) .setSocketTimeout(dzhlConfig.getReadTimeout()) .build()) .build(); } 该代码下载文件报 请求超时错误 文件下载不下来 为什么?
06-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值