session.clear()与session.flush的区别

本文介绍了Hibernate框架中session缓存的两种关键操作:session.clear()用于清空缓存中的实体对象,而session.flush()则用于同步缓存中的实体对象与数据库记录,确保数据一致性。通过示例演示了这两种方法如何影响SQL语句的执行次数。

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

session.clear()方法负责清空session缓存中的实体对象,不管缓存中数据是否与数据库记录一致;

session.flush()方法负责同步session缓存中的实体对象与数据库记录,保持数据的一致性。

@Test
public void testSessionClear(){
	Session session = sessionFactory.openSession();
	session.beginTransaction();
	Teacher t = (Teacher) session.get(Teacher.class, 3);
	System.out.println(t.getName());
	session.clear();//这里不clear只会执行一次sql语句,有clear会执行2次
	Teacher t2 = (Teacher) session.get(Teacher.class, 3);
	System.out.println(t2.getName());
	session.getTransaction().commit();
}

如果没有调用session.clear(),由于一级缓存的存在,查询同样的记录,只会发出一条sql语句;如果清空了session缓存,那么这里会发出2条sql语句。


@Test
public void testFlush()
{
	Session session =  sessionFactory.openSession();
	session.beginTransaction();
	Teacher t = (Teacher) session.get(Teacher.class, 3);
	t.setName("xiu");
	session.flush();//有flush会执行2次UPDAE,没有会只执行一次
	t.setName("aty");
	session.getTransaction().commit();
}
如果不执行flush,只会发出一条update语句;执行了flush,会发出2条update语句,也就是说flush会强制缓存与数据库数据同步

package com.example.demo.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.thread.ThreadFactoryBuilder; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.dao.entity.WExecuteHost; import com.example.demo.dao.entity.WPersonalHost; import com.example.demo.dao.mapper.WExecuteHostMapper; import com.example.demo.service.WExecuteHostService; import com.example.demo.service.WPersonalHostService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Service public class WExecuteHostServiceImpl extends ServiceImpl<WExecuteHostMapper, WExecuteHost> implements WExecuteHostService { @Autowired private WPersonalHostService wPersonalHostService; @Data private static class UserSession { private Process cmdProcess; private volatile boolean isProcessRunning; private String sessionId; private String logFilePath; private final Queue<String> logBuffer = new ConcurrentLinkedQueue<>(); private static final int BATCH_SIZE = 50; // 批量写入阈值 } private final Map<String, UserSession> userSessions = new ConcurrentHashMap<>(); private final SimpMessagingTemplate messagingTemplate; // 异步日志写入线程池 private static final ExecutorService LOG_WRITER_POOL = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNamePrefix("log-writer-").build()); // 日志刷新调度器 private static final ScheduledExecutorService LOG_FLUSH_SCHEDULER = Executors.newSingleThreadScheduledExecutor(); public WExecuteHostServiceImpl(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; startLogFlushService(); } // 初始化日志刷新服务 private void startLogFlushService() { LOG_FLUSH_SCHEDULER.scheduleAtFixedRate(() -> { userSessions.values().forEach(session -> { if (!session.getLogBuffer().isEmpty()) { List<String> batch = CollUtil.newArrayList(session.getLogBuffer()); session.getLogBuffer().clear(); asyncWriteLog(session.getLogFilePath(), String.join("\n", batch)); } }); }, 0, 1, TimeUnit.SECONDS); } @Override public String pipList() { // 示例实现:返回模拟的管道列表(实际需根据业务需求实现) return "[]"; } @Override public void executeCommand(String command, String executeHost, String userId) { // 1. 权限校验 if (!validateUserHost(userId, executeHost)) { sendError("无权访问该主机", userId); return; } // 2. 初始化日志系统 UserSession session = userSessions.computeIfAbsent(userId, key -> { if (userSessions.containsKey(userId)) { sendError("已有命令执行中,请等待完成", userId); return null; } return createNewSession(userId, executeHost); }); if (session == null) return; // 3. 命令处理 if ("ABORT".equalsIgnoreCase(command)) { handleAbort(userId); return; } // 写入命令到日志 session.getLogBuffer().offer( "\n———————— 执行时间 " + DateUtil.now() + " ————————\n[<<Input<<] " + command); // 发送命令到进程 IoUtil.write(session.getCmdProcess().getOutputStream(), "GBK", false, command + "\n"); } private boolean validateUserHost(String userId, String executeHost) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(WPersonalHost::getP13, userId) .eq(WPersonalHost::getExecuteHost, executeHost) .eq(WPersonalHost::getState, "在线"); return wPersonalHostService.getOne(queryWrapper) != null; } private UserSession createNewSession(String userId, String executeHost) { try { UserSession session = new UserSession(); session.setSessionId(UUID.randomUUID().toString()); session.setLogFilePath(initLogFile(userId, executeHost)); // 启动CMD进程(带唯一标题) ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/k", "title " + generateUniqueTitle(userId)); session.setCmdProcess(pb.redirectErrorStream(true).start()); // 启动输出监听线程 startOutputThread(session, userId); return session; } catch (IOException e) { sendError("进程启动失败: " + e.getMessage(), userId); return null; } } private String initLogFile(String userId, String executeHost) { // 1. 构建基础路径(使用File.separator) String baseDir = FileUtil.normalize( System.getProperty("user.dir") + File.separator + "command-log"); // 2. 构建安全路径(统一使用File.separator) String safePath = FileUtil.normalize( baseDir + File.separator + userId + File.separator + executeHost + File.separator + "项目名称"); // 3. 安全校验(现在路径分隔符一致) if (!safePath.startsWith(baseDir)) { throw new SecurityException("非法日志路径: " + safePath); } // 4. 创建目录(自动处理路径分隔符) FileUtil.mkdir(safePath); // 5. 生成日志文件 String logFileName = DateUtil.today() + ".log"; return FileUtil.touch(safePath + File.separator + logFileName).getAbsolutePath(); } private void startOutputThread(UserSession session, String userId) { new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(session.getCmdProcess().getInputStream(), "GBK"))) { String line; while ((line = reader.readLine()) != null) { // WebSocket推送 messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, line); // 日志缓冲 session.getLogBuffer().offer(line); if (session.getLogBuffer().size() >= UserSession.BATCH_SIZE) { asyncWriteLog(session.getLogFilePath(), String.join("\n", session.getLogBuffer())); session.getLogBuffer().clear(); } } } catch (Exception e) { sendError("输出流异常: " + e.getMessage(), userId); } finally { cleanupSession(userId); } }).start(); } private void asyncWriteLog(String logFilePath, String content) { CompletableFuture.runAsync(() -> { try { FileUtil.appendString(content + System.lineSeparator(), logFilePath, "GBK"); } catch (Exception e) { System.err.println("日志写入失败: " + e.getMessage()); } }, LOG_WRITER_POOL); } private void cleanupSession(String userId) { UserSession session = userSessions.remove(userId); if (session != null) { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } // 强制将剩余日志写入文件(新增代码) if (!session.getLogBuffer().isEmpty()) { asyncWriteLog(session.getLogFilePath(), String.join("\n", session.getLogBuffer())); session.getLogBuffer().clear(); } } catch (Exception ignored) {} } } @PreDestroy public void cleanup() { LOG_FLUSH_SCHEDULER.shutdown(); LOG_WRITER_POOL.shutdown(); userSessions.forEach((userId, session) -> { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } } catch (Exception ignored) { } }); userSessions.clear(); } public void handleAbort(String userId) { UserSession session = userSessions.get(userId); if (session != null && session.isProcessRunning()) { session.getCmdProcess().destroyForcibly(); sendError("⏹ 用户命令已终止", userId); } else { sendError("⏹ 无执行中命令", userId); } } private void sendError(String message, String userId) { try { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "❌ " + message); } catch (Exception ignored) { } } private String generateUniqueTitle(String userId) { return "CMD_SESSION_" + userId + "_" + System.currentTimeMillis(); } }能不能在命令执行结束的时候,追加一行“命令执行结束”返回前端页面
最新发布
06-07
package com.example.demo.service.impl; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.dao.entity.WExecuteHost; import com.example.demo.dao.entity.WPersonalHost; import com.example.demo.dao.mapper.WExecuteHostMapper; import com.example.demo.service.WExecuteHostService; import com.example.demo.service.WPersonalHostService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.file.Files; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** * @author fangpeiyuan * @description 针对表【w_execute_host(执行主机表)】的数据库操作Service实现 * @createDate 2025-06-06 09:32:45 */ @Service public class WExecuteHostServiceImpl extends ServiceImpl<WExecuteHostMapper, WExecuteHost> implements WExecuteHostService { @Autowired private WPersonalHostService wPersonalHostService; @Data private static class UserSession { private Process cmdProcess; private BufferedWriter commandWriter; private BufferedWriter logWriter; // 添加日志写入器 private volatile boolean isProcessRunning; private String sessionId; private String logFilePath; } // 替换全局变量为会话映射 private final Map<String, UserSession> userSessions = new ConcurrentHashMap<>(); private final SimpMessagingTemplate messagingTemplate; public WExecuteHostServiceImpl(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; } @Override public String pipList() { return null; } @Override public void executeCommand(String command, String executeHost, String userId) { // 判断这个host是否属于该用户并状态是活跃的 LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); //todo 后续动态 queryWrapper .eq(WPersonalHost::getP13, userId) .eq(WPersonalHost::getExecuteHost, executeHost) .eq(WPersonalHost::getState, "在线"); WPersonalHost wPersonalHost = wPersonalHostService.getOne(queryWrapper); if (ObjectUtil.isEmpty(wPersonalHost)) { System.out.println("该主机并不属于当前用户,请联系管理员处理。"); return; } // 初始化日志目录 // todo 后面动态 String logDir = System.getProperty("user.dir") + File.separator + "command-log" + File.separator + userId + File.separator + executeHost + File.separator + "项目名称"; // 创建目录(如果不存在) File logDirectory = new File(logDir); if (!logDirectory.exists()) { boolean created = logDirectory.mkdirs(); if (!created) { System.err.println("无法创建日志目录: " + logDir); return; // 或者抛出异常 } System.out.println("日志目录已创建: " + logDir); } // 按天生成日志文件名 String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); String logFilePath = logDir + File.separator + dateStr + ".log"; try { // 确保日志目录存在 Files.createDirectories(Paths.get(logDir)); // ABORT 命令处理 if ("ABORT".equalsIgnoreCase(command)) { try (BufferedWriter logWriter = new BufferedWriter(new FileWriter(logFilePath, true))) { logWriter.write("\n———————— 执行时间 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " ————————\n"); logWriter.write("[<<Input<<] ABORT command received\n"); } catch (IOException e) { // 错误处理 } handleAbort(userId); return; } // 获取或创建用户专属会话 UserSession session = userSessions.computeIfAbsent(userId, key -> { if (userSessions.containsKey(userId)) { sendError("已有命令执行中,请等待完成", userId); return null; } try { UserSession newSession = new UserSession(); newSession.setSessionId(UUID.randomUUID().toString()); // 初始化专属进程 (关键修改点) String uniqueTitle = "User_" + userId + "_" + System.currentTimeMillis(); newSession.setCmdProcess(new ProcessBuilder("cmd.exe", "/k", "title " + uniqueTitle) .redirectErrorStream(true).start()); newSession.setCommandWriter( new BufferedWriter(new OutputStreamWriter( newSession.getCmdProcess().getOutputStream(), "gbk")) ); // 创建并绑定日志写入器 newSession.setLogWriter(new BufferedWriter(new FileWriter(logFilePath, true))); newSession.getLogWriter().write("\n———————— 执行时间 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " ————————\n"); // 启动输出线程(传递日志写入器) startOutputThread(newSession, userId); return newSession; } catch (IOException e) { sendError("进程启动失败: " + e.getMessage(), userId); return null; } }); // 写入输入命令 try { assert session != null; session.getLogWriter().write("[<<Input<<] " + command + "\n"); session.getLogWriter().flush(); session.getCommandWriter().write(command + "\n"); session.getCommandWriter().flush(); } catch (IOException e) { sendError("命令发送失败: " + e.getMessage(), userId); } } catch (IOException e) { e.printStackTrace(); } } // 独立的输出线程方法 private void startOutputThread(UserSession session, String userId) { new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(session.getCmdProcess().getInputStream(), "gbk"))) { String line; while ((line = reader.readLine()) != null) { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, line); // 直接使用会话的日志写入器 synchronized (session) { session.getLogWriter().write(line + "\n"); session.getLogWriter().flush(); } } } catch (Exception e) { sendError("输出流异常: " + e.getMessage(), userId); } finally { try { // 安全关闭日志写入器 if (session.getLogWriter() != null) { session.getLogWriter().close(); } } catch (IOException e) { // 错误处理 } session.setProcessRunning(false); userSessions.remove(userId); } }).start(); } public void handleAbort(String userId) { UserSession session = userSessions.get(userId); if (session != null && session.isProcessRunning()) { session.getCmdProcess().destroyForcibly(); sendError("⏹ 用户命令已终止", userId); } else { sendError("⏹ 无执行中命令", userId); } } private void sendError(String message, String userId) { try { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "❌ " + message); } catch (Exception ignored) { } } @PreDestroy public void cleanup() { userSessions.forEach((userId, session) -> { try { if (session.getLogWriter() != null) { session.getLogWriter().close(); } if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } } catch (IOException e) { System.err.println("清理会话资源时出错: " + e.getMessage()); } }); userSessions.clear(); } } 这里的日志写入是不是有什么依赖可以更好地实现?如果一个websocket长时间没有使用能否帮它断开
06-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值