首页/新闻资讯/正文详情

JDBC_day3|Service 业务层|三层架构|JDBC 事务|ThreadLocal 线程绑定连接

发布时间:2026/9/26 7:46:38 来源:云帆数科 栏目:资讯中心
JDBC_day3|Service 业务层|三层架构|JDBC 事务|ThreadLocal 线程绑定连接
一、Service业务层概述1.1 什么是业务层 ServiceDAO 层只负责单纯数据库 CRUD 操作Service 业务层用来实现完整业务功能面向用户提供业务能力。用户每一次操作对应一个业务功能用户办理账户转账业务用户注册账号网站首页数据展示用户登录校验Service 层主要职责可以调用 DAO 层 1 次或者多次数据库操作完成业务数据运算、逻辑判断管理事务核心保证一组数据库操作要么全部成功要么全部失败。开发规范业务接口命名XXXService业务实现类命名XXXServiceImpl原则一张业务表对应一套 Service 接口 实现类。业务分层关系用户交互视图层 →Service 业务层→ DAO 数据访问层 → 数据库二、实战案例账户转账业务2.1 业务需求A 账户向 B 账户转账扣减转出账户余额增加转入账户余额。 业务执行步骤校验转出账户登录信息获取转账金额转出账户扣钱update校验转入账户信息转入账户加钱update。⚠️问题如果扣钱之后程序突然异常崩溃加钱操作没有执行。会出现钱凭空消失 解决方案事务把两次 update 放到同一个事务中要么全部成功要么全部回滚。2.2 开发整体步骤三层开发标准流程创建数据库账户表编写实体类 Account和数据库表字段一一对应DAO 层编写AccountDao接口 AccountDaoImpl实现类封装增删改查Service 层编写AccountService接口 AccountServiceImpl实现类编写转账业务控制事务JUnit 单元测试测试业务功能。2.3 初步编写转账业务原生事务控制JDBC 默认事务是自动提交模式每执行一条 SQL 就自动 commit。 事务完全由Connection连接对象进行管理。表格Connection 事务方法说明conn.setAutoCommit(false)关闭自动提交开启手动事务写在业务最开始conn.commit()业务全部正常手动提交事务持久化所有修改conn.rollback()出现异常回滚事务撤销全部修改基础 service 伪代码Override public void transfer(Integer fromCardId, String password, Double money, Integer toCardId) { Connection conn null; try { // 1.获取连接关闭自动提交开启手动事务 conn JDBCUtil.getConn(); conn.setAutoCommit(false); AccountDao dao new AccountDaoImpl(); //业务逻辑 //①校验转出账户 Account fromAcc dao.selectByCardId(fromCardId); //②校验密码、余额判断 //③转出账户扣钱 //④转入账户加钱 //全部业务执行完毕提交事务 conn.commit(); } catch (Exception e) { try { //发生任何异常执行回滚 conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); throw new RuntimeException(转账业务失败,e); }finally { JDBCUtil.close(null,conn); } }2.4 重大坑点事务控制失效❌现象Service 开启事务调用 DAO 的多个 update一旦出现异常不会回滚事务失效。✅根因Service 层拿了一个 Connection 连接 DAO 层内部调用JDBCUtil.getConn()拿到全新的另外一个 Connection 对象。 多个 DAO 使用不同的数据库连接每个连接各自独立事务事务完全隔离Service 的 commit/rollback 管不到 DAO 的连接。两种解决方案方案一把 Connection 当做参数传入 DAO 方法不推荐DAO 所有 CRUD 方法增加参数Connection connService 把自己的连接传给 DAO 使用。缺点污染 DAO 接口每一个 DAO 方法都要多传连接参数代码侵入严重业务代码到处传递 conn耦合高。并且 DAO 不能内部关闭连接连接要交给 Service 层关闭。方案二ThreadLocal 线程本地变量【推荐】核心思路把 Connection 绑定到当前线程上同一个线程中不管 Service、DAO调用工具类都拿到同一个 Connection 对象。 线程之间互相隔离A 线程的连接不会被 B 线程拿到。三、ThreadLocal 原理 改造 JDBCUtil 工具类3.1 ThreadLocal 是什么ThreadLocalT线程本地变量。将对象绑定到当前执行线程同一个线程任意位置调用get()都拿到同一个对象不同线程之间数据互相隔离互不干扰业务结束调用remove()释放线程绑定对象防止内存泄漏。核心 API表格方法功能set(obj)将对象绑定到当前线程get()获取当前线程绑定的对象remove()删除当前线程绑定的对象释放资源3.2 重构 JDBCUtil 工具类增加 ThreadLocalimport java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; public class JDBCUtil2 { //ThreadLocal绑定当前线程的Connection对象 private static ThreadLocalConnection tol new ThreadLocal(); private static Properties prop new Properties(); static { try { InputStream in JDBCUtil2.class.getResourceAsStream(/jdbc.properties); prop.load(in); Class.forName(prop.getProperty(jdbc.driverClassName)); }catch (Exception e){ throw new RuntimeException(配置文件加载失败,e); } } /** * 获取数据库连接同一个线程多次调用返回同一个connection */ public static Connection getConn(){ Connection conn tol.get(); if(conn null){ //当前线程还没有绑定连接新建连接 try { conn DriverManager.getConnection( prop.getProperty(jdbc.url), prop.getProperty(jdbc.username), prop.getProperty(jdbc.password) ); //绑定到当前线程 tol.set(conn); }catch (Exception e){ throw new RuntimeException(获取数据库连接失败,e); } } return conn; } /** * 关闭资源 * 重点如果conn来自ThreadLocal关闭之后必须执行tol.remove() */ public static void close(ResultSet rs, Statement stm, Connection conn){ try { if(rs ! null) rs.close(); if(stm ! null) stm.close(); if(conn ! null){ conn.close(); //清除当前线程绑定的连接释放防止内存泄漏 tol.remove(); } }catch (Exception e){ e.printStackTrace(); } } //重载没有resultSet场景DAO增删改调用 public static void close(Statement stm,Connection conn){ close(null,stm,conn); } }重要约束DAO 层代码不可以关闭 Connection 连接由 Service 业务层统一关闭DAO 只关闭 Statement/ResultSet。 只有业务全部结束才执行 close同时执行tol.remove()清除线程绑定。3.3 改造之后事务完整流程Service 业务层调用JDBCUtil2.getConn()拿到连接绑定当前线程conn.setAutoCommit(false)关闭自动提交开启手动事务Service 多次调用 DAO 层DAO 内部调用JDBCUtil2.getConn()拿到线程绑定的同一个连接业务全部正常Service 执行conn.commit()提交捕获异常Service 执行conn.rollback()回滚finally 调用 close关闭连接tol.remove()清除线程绑定。✅ 现在所有 DAO 复用同一个 Connection事务完全生效拓展Spring 框架底层事务管理本质原理也是依靠 ThreadLocal 绑定数据库连接。四、JavaWeb 经典三层架构表格分层职责重点说明视图层 View和用户交互接收输入展示结果Swing / Servlet / 前端页面业务层 Service实现业务逻辑、数据运算、事务控制接口 实现类调用 DAO 层事务在这里开启、提交、回滚数据访问层 DAO只负责数据库 CRUD 增删改查不处理业务不处理事务不能关闭 Connection 连接三层架构开发好处各司其职职责分离解耦合修改某一层其他层不受影响利于团队分工协作开发方便单元测试。开发顺序建表 → 实体类 → DAO 接口 实现类 → Service 接口 实现类 → JUnit 单元测试。五、动手波Account 账户业务5.1 定义 AccountService 业务接口import java.util.List; public interface AccountService { // 查询全部账户 ListAccount queryAll(); //新增账户 void addAccount(Account acc); //修改账户 void modifyAccount(Account acc); //删除账户 void removeAccount(Integer cardId); //根据卡号查询账户 Account queryByCardId(Integer cardId); /** * 多条件动态查询 * username、balance、mobile参数不为空就作为查询条件为空不参与条件拼接 */ ListAccount queryByTiaojian(String username, Double balance, String mobile); //转账业务 void transfer(Integer fromCardId,String password,Double money,Integer toCardId); }可以尝试编写AccountServiceImpl实现上述全部接口使用重构后的JDBCUtil2ThreadLocal 管理连接转账业务完整实现事务使用 JUnit 单元测试每一个业务方法。六、重点知识总结Service 业务层实现业务逻辑事务写在 Service 层不是 DAO 层JDBC 默认自动提交事务setAutoCommit(false)开启手动事务commit()提交rollback()回滚事务失效最常见原因多次获取不同 Connection 连接ThreadLocal绑定对象到当前线程线程隔离同一个线程拿到同一个 ConnectionDAO 层只关闭 Statement、ResultSetConnection 交给 Service 层关闭同时调用 remove 清除线程绑定防止内存泄漏三层架构View 视图层 / Service 业务层 / DAO 数据访问层开发顺序表→实体→DAO→Service→单元测试。 以上为本篇 JDBC 学习笔记若有更好的建议欢迎评论区商讨交流欢迎各位大佬指正、补充不同实现思路。

相关推荐

通达信趋势指标智能生命线:双EMA+ATR动态通道实战详解
通达信趋势指标智能生命线:双EMA+ATR动态通道实战详解

做趋势交易的人兜兜转转,最后大概率都会绕回同一类工具:均线。但你真拿一条普通均线去实战,就会发现在震荡行情里它能把人折腾到怀疑人生。我前阵子花了不少时间研究怎么把"均线趋势跟踪"做得更聪明一点,最后折腾出一套… · 2026/9/26 7:46:38

C++构造函数详解:从默认初始化到拷贝移动,彻底搞懂对象生命周期
C++构造函数详解:从默认初始化到拷贝移动,彻底搞懂对象生命周期

构造函数这玩意儿,教科书上写得比说明书还干,什么“用于初始化对象的成员变量”之类的话,背下来了也不知道它到底在忙什么。我当年学C的时候,构造函数这关就是靠死记硬背混过去的,直到后来自己在项目里写崩了几次、排查… · 2026/9/26 7:46:38

claude-code-templates:MCP配置模板化,解决Claude Code环境配置痛点
claude-code-templates:MCP配置模板化,解决Claude Code环境配置痛点

1. 从一堆散落的配置说起:claude-code-templates 到底在解决什么如果你最近在折腾 Claude Code,大概率经历过这样的场景:装完 CLI,配好 API Key,兴致勃勃想让它帮你写点东西,结果发现它默认的能力边界比想象… · 2026/9/26 7:46:32

Java+原生双端+小程序全栈零售系统工程实践
Java+原生双端+小程序全栈零售系统工程实践

简介:这是一套面向Java开发者与移动应用全栈工程师的成人健康电商零售系统源码,聚焦两性健康产品线上销售场景,提供安卓、iOS双端原生APP及微信小程序三位一体解决方案,适用于快速搭建合规化私域零售平台或二次开发学习。资源包共… · 2026/9/26 8:21:25

大促“历史最低价”是真是假?用价格曲线拆穿折扣套路
大促“历史最低价”是真是假?用价格曲线拆穿折扣套路

十一月刚过完,各大平台就开始放大促战报:"新史低"三个字刷得满天飞,什么"低至2折"、"全年最低"、"错过再等一年"轮番打在首页上。我盯着后台跳出来的价格提醒看了半天,又翻了翻近半年的历… · 2026/9/26 8:21:19

Claude Code模板化实战:CLAUDE.md与提示词模板搭建指南
Claude Code模板化实战:CLAUDE.md与提示词模板搭建指南

开头:别再逼AI猜你的项目意图了如果你最近用过 Claude Code,大概率会有同感:它在终端里干活麻利是真麻利,但偶尔也会跑偏——你以为它知道项目结构,它其实在按“一般情况”瞎猜;你以为它记得之前定的规范&a… · 2026/9/26 8:21:19

Ternary Bonsai 27B:三值量化+树状稀疏注意力的本地大模型新范式
Ternary Bonsai 27B:三值量化+树状稀疏注意力的本地大模型新范式

1. 为什么是Ternary Bonsai 27B?——不是又一个“小而美”模型,而是三值量化与结构精简的双重突破Ternary Bonsai 27B 这个名字里,“Ternary”和“Bonsai”两个词就直接点破了它的核心设计哲学。它不是在现有大模型基础上简单剪枝或蒸馏出来的… · 2026/9/26 8:21:07

如何自己动手改配列板:用Keychron-Keyboards-Hardware-Design的DXF Plate文件完成第一次Mod实战
如何自己动手改配列板:用Keychron-Keyboards-Hardware-Design的DXF Plate文件完成第一次Mod实战

如何自己动手改配列板:用Keychron-Keyboards-Hardware-Design的DXF Plate文件完成第一次Mod实战 【免费下载链接】Keychron-Keyboards-Hardware-Design Industrial design files for Keychron keyboards and mice. 100 models with CAD assets in STEP, DXF, DWG, a… · 2026/9/26 8:21:07

OpCore-Simplify:导出一份硬件报告,就能生成 OpenCore EFI
OpCore-Simplify:导出一份硬件报告,就能生成 OpenCore EFI

OpCore-Simplify:导出一份硬件报告,就能生成 OpenCore EFI 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 在 PC 上装 macOS&a… · 2026/9/26 8:21:01

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码