当前位置: 首页 > news >正文

Spring JDBC实战指南:从基础操作到事务管理全解析

在Java企业级开发中,数据持久层的实现始终是核心环节之一。原生JDBC API虽然功能完备,但繁琐的资源管理(如连接创建与关闭)、重复的异常处理代码,往往会增加开发工作量并降低代码可读性。Spring框架提供的Spring JDBC模块,通过对原生JDBC的封装,巧妙解决了这些痛点,让开发者能够以更简洁、高效的方式完成数据库操作。本文将从核心组件解析入手,逐步展开Spring JDBC的基础CRUD实现,最后通过转账案例深入讲解事务管理的应用,带你完整掌握这一实用技术。

一、Spring JDBC核心组件:JdbcTemplate深度解析

Spring JDBC的核心优势集中体现在JdbcTemplate类上。作为Spring提供的操作模板类之一,JdbcTemplate对原生JDBC API进行了轻量级封装,无需开发者关注数据库连接的创建、释放以及SQL执行过程中的异常处理,而是将核心精力聚焦于业务SQL的编写。

JdbcTemplate提供了覆盖各类数据库操作的方法,按功能可分为四大核心类型,适配不同的业务场景:

  1. execute方法:通用性最强,可执行任意SQL语句,尤其适合执行数据定义语言(DDL),如创建表、删除表等操作。

  2. update与batchUpdate方法:专门用于执行数据操纵语言(DML)中的新增、修改、删除操作。其中batchUpdate方法支持批量处理,能有效提升批量数据操作的效率。

  3. query与queryForXXX方法:负责数据查询操作。query方法可返回多条数据并封装为集合,queryForXXX系列方法(如queryForObject、queryForInt)则适用于单条数据查询或特定类型结果的查询。

  4. call方法:用于调用数据库中的存储过程或函数,简化了复杂数据库逻辑的调用流程。

简单来说,日常开发中的增删改操作主要依赖update和batchUpdate方法,查询操作则通过query系列方法实现,而execute和call方法则分别适配DDL操作和存储过程调用场景。

二、Spring JDBC实操流程:从环境搭建到CRUD实现

接下来我们通过完整的实操案例,一步步实现Spring JDBC的基础数据库操作。本次案例将基于MySQL数据库,实现用户表的增删改查功能,整体流程分为环境准备、配置文件编写、实体类定义、操作实现四个环节。

2.1 环境准备:数据库与依赖配置

首先需要创建对应的数据库和数据表,并引入Spring JDBC及MySQL驱动相关依赖。

2.1.1 创建数据库与数据表

执行以下SQL语句,创建mybatis_demo数据库和user表,并插入测试数据:

create database mybatis_demo; use mybatis_demo; CREATE TABLE `user` ( `id` int(11) NOT NULL auto_increment, `username` varchar(32) NOT NULL COMMENT '用户名称', `birthday` datetime default NULL COMMENT '生日', `sex` char(1) default NULL COMMENT '性别', `address` varchar(256) default NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'老王','2018-02-27 17:47:08','男','北京'),(2,'熊大','2018-03-02 15:09:37','女','上海'),(3,'熊二','2018-03-04 11:34:34','女','深圳'),(4,'光头强','2018-03-04 12:04:06','男','广州');
2.1.2 引入核心依赖

在Maven项目的pom.xml文件中,引入Spring JDBC核心依赖和MySQL驱动依赖:

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.21</version> </dependency> <!--mysql驱动包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency>

2.2 核心配置:application.xml文件编写

Spring JDBC的核心配置主要包括数据源和JdbcTemplate的配置。数据源用于管理数据库连接信息,JdbcTemplate则依赖数据源完成数据库操作。在resources目录下创建application.xml文件,配置内容如下:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置连接DriverManagerDataSource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?characterEncoding=utf8&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="2020"/> </bean> <!-- 配置jdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>

说明:此处使用Spring提供的DriverManagerDataSource作为数据源,适合开发环境使用。在生产环境中,更推荐使用Druid、C3P0等成熟的数据源连接池,以提升连接管理效率和系统稳定性。

2.3 实体类定义:User类

创建User实体类,对应数据库中的user表字段,提供getter和setter方法用于属性的赋值与获取:

public class User { private Integer id; private String username; private Date birthday; private String sex; private String address; //get set方法 };

2.4 CRUD操作实现:SpringTest测试类

通过Junit测试类实现用户表的增删改查操作。首先通过Spring上下文加载配置文件,获取JdbcTemplate实例,再调用其对应的方法完成具体操作:

import com.qcby.entity.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import java.util.List; public class SpringTest { ApplicationContext ctx=new ClassPathXmlApplicationContext("Application.xml"); JdbcTemplate jdbcTemplate= (JdbcTemplate) ctx.getBean("jdbcTemplate"); @Test public void testInsert(){ String sql="insert into user(username,address) values('李连杰','上海')"; jdbcTemplate.execute(sql); } @Test public void testUpdate(){ String sql="update user set username='稳杰',address='南海' where id=?"; int res=jdbcTemplate.update(sql,2); System.out.println(res); } @Test public void testDelete(){ String sql="delete from user where id=?"; int res=jdbcTemplate.update(sql,18); System.out.println(res); } //查询列表 @Test public void testQueryList(){ String sql = "select * from user where address like '%京%'"; List<User> userList= (List<User>) jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(User.class)); System.out.println("查询List: "); for (User user : userList) { System.out.println(user); } System.out.println("数量: "+userList.size()); } }

关键说明:query方法中使用BeanPropertyRowMapper实现查询结果到User实体类的自动映射,要求实体类属性名与数据库表字段名一致(大小写不敏感),极大简化了结果集的处理流程。

三、进阶实战:转账案例中的事务管理

在实际业务场景中,很多操作需要保证原子性,即一系列操作要么全部成功,要么全部失败。例如转账操作,从A账户扣款和向B账户加款两个步骤必须同时完成,否则会出现数据不一致问题。接下来通过模拟支付宝转账案例,讲解Spring事务管理的实现方式。

3.1 问题场景:未加事务的转账漏洞

模拟场景:张三、李四账户初始余额各2000元,张三向李四转账500元。若转账过程中出现异常,会导致张三账户扣款成功,但李四账户未加款,出现数据不一致。

3.1.1 创建账户表
create table alipay( aliname varchar (60), amount double );

插入测试数据:insert into alipay(aliname, amount) values('张三', 2000), ('李四', 2000);

3.1.2 编写DAO层接口与实现类

创建IAccountDao接口,定义转账方法:

public interface AlipayDao { public void transfer(String fromA,String toB,int amount); }

实现类AlipayDaoImpl,依赖JdbcTemplate完成转账操作,并在两个步骤中间模拟异常:

import com.qcby.dao.AliPayDao; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public class AliPayDaoImpl implements AliPayDao { private JdbcTemplate jdbcTemplate; public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void transfer(String fromA, String toB, Double amount) { jdbcTemplate.update("update alipay set amount = amount-? where aliname = ?",amount,fromA); Integer a = Integer.valueOf("你好"); //手动实现错误情况 jdbcTemplate.update("update alipay set amount = amount+? where aliname = ?",amount,toB); } }
3.1.3 测试类与配置

配置文件applicationContext.xml(未加事务):

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置连接DriverManagerDataSource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/> <property name="username" value="root"/> <property name="password" value="2020"/> </bean> <!-- 配置jdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="AliPayDaoImpl" class="com.qcby.dao.impl.AliPayDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> </beans>

测试类TestAlipay:

import java.util.List; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.hh.dao.AlipayDao; public class TestAlipay { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); AlipayDao alipayDao=(AlipayDao) context.getBean("alipayDao"); alipayDao.transfer("张三", "李四", 500); } }

运行测试:取消实现类中模拟异常的代码注释,执行后会发现张三账户余额减少500元,而李四账户余额未变化,数据出现不一致。这就是未加事务管理导致的问题。

3.2 解决方案:Spring事务管理实现

Spring提供了两种主流的事务管理方式:基于XML配置的声明式事务和基于注解的声明式事务。声明式事务无需在业务代码中编写事务管理逻辑,通过配置即可实现,是企业开发中的首选方式。

3.2.1 基于XML配置的事务管理

基于XML的方式需要配置事务管理器、事务通知和AOP切入点,将事务逻辑切入到目标方法中。修改后的配置文件如下:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置连接DriverManagerDataSource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/> <property name="username" value="root"/> <property name="password" value="2020"/> </bean> <!-- 配置jdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="AliPayDaoImpl" class="com.qcby.dao.impl.AliPayDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> <!-- 定义事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 编写事务通知 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" /> <!-- <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="search*" propagation="SUPPORTS" read-only="true"/> <tx:method name="select*" propagation="SUPPORTS" read-only="true"/> <tx:method name="find*" propagation="SUPPORTS" read-only="true"/> <tx:method name="get*" propagation="SUPPORTS" read-only="true"/> --> </tx:attributes> </tx:advice> <!-- 编写AOP,让spring自动将事务切入到目标切点 --> <aop:config> <!-- 定义切入点 --> <aop:pointcut id="txPointcut" expression="execution(* com.qcby.dao.impl.AliPayDaoImpl.transfer(..))" /> <!-- 将事务通知与切入点组合 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" /> </aop:config> </beans>
3.2.2 基于注解的事务管理

基于注解的方式更加简洁,只需在配置文件中开启事务注解驱动,然后在需要事务管理的方法或类上添加@Transactional注解即可。

第一步:修改配置文件,开启事务注解驱动:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置连接DriverManagerDataSource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/> <property name="username" value="root"/> <property name="password" value="2020"/> </bean> <!-- 定义事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 开启事务注解驱动 --> <tx:annotation-driven transaction-manager="txManager"/> </beans>

第二步:在transfer方法上添加@Transactional注解:

import com.qcby.dao.AliPayDao; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public class AliPayDaoImpl implements AliPayDao { private JdbcTemplate jdbcTemplate; public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false) public void transfer(String fromA, String toB, Double amount) { jdbcTemplate.update("update alipay set amount = amount-? where aliname = ?",amount,fromA); Integer a = Integer.valueOf("你好"); jdbcTemplate.update("update alipay set amount = amount+? where aliname = ?",amount,toB); } }

测试验证:再次执行测试类,当模拟异常出现时,Spring会自动回滚事务,张三和李四的账户余额均保持初始状态,数据一致性得到保障。

3.3 核心知识点:事务传播行为与隔离级别

在事务配置中,传播行为和隔离级别是两个核心参数,决定了事务的运行规则。

3.3.1 事务传播行为(Propagation)

传播行为定义了事务方法被另一个事务方法调用时的执行策略,常用值包括:

  • REQUIRED(默认):如果当前存在事务,则加入该事务;如果没有事务,则创建新事务。

  • SUPPORTS:如果当前存在事务,则加入该事务;如果没有事务,则以非事务方式运行。

  • MANDATORY:必须在事务中运行,否则抛出异常。

  • REQUIRES_NEW:创建新事务,若当前存在事务则挂起。

  • NOT_SUPPORTED:以非事务方式运行,若当前存在事务则挂起。

  • NEVER:必须以非事务方式运行,否则抛出异常。

  • NESTED:如果当前存在事务,则在嵌套事务中运行;否则创建新事务。

3.3.2 事务隔离级别(Isolation)

隔离级别定义了事务之间的隔离程度,隔离级别越高,数据一致性越好,但并发性能越低。Spring支持的隔离级别包括:

  • DEFAULT:使用数据库默认隔离级别(MySQL默认REPEATABLE_READ)。

  • READ_UNCOMMITTED:允许脏读、不可重复读和幻读,隔离级别最低。

  • READ_COMMITTED:避免脏读,允许不可重复读和幻读。

  • REPEATABLE_READ:避免脏读和不可重复读,允许幻读。

  • SERIALIZABLE:最高隔离级别,避免所有数据不一致问题,但并发性能最差。

四、总结与拓展

本文从Spring JDBC的核心组件JdbcTemplate入手,详细讲解了其四大类核心方法的应用场景,通过完整的实操案例实现了用户表的增删改查操作,最后结合转账案例深入剖析了Spring事务管理的两种实现方式及核心知识点。Spring JDBC通过简洁的API设计,极大降低了原生JDBC的使用难度,而声明式事务管理则让开发者无需关注事务的底层实现,只需通过简单配置即可保障数据一致性。

拓展建议:在实际开发中,可结合Spring的依赖注入(DI)和控制反转(IOC)特性,进一步优化代码结构;生产环境中替换为成熟的数据源连接池(如Druid)提升系统性能;对于复杂的业务场景,可深入学习Spring事务的嵌套使用、异常回滚策略等高级特性,让技术应用更加灵活高效。

http://www.jsqmd.com/news/197313/

相关文章:

  • Langchain4j-文档处理和 RAG 流程分析
  • Grafana仪表盘模板分享:可视化系统健康状态
  • 伦理审查机制:确保技术向善发展
  • 通义千问语音版底层技术曝光:源自Fun-ASR架构优化
  • Opencv总结8——停车场项目实战
  • 2025年秦皇岛榻榻米定制品牌综合评估与推荐榜单 - 2025年品牌推荐榜
  • API调用频率限制:每分钟最多100次请求
  • 清除浏览器缓存后仍显示异常?可能是Fun-ASR版本问题
  • 语音端点检测精度达95%:VAD模块独立使用价值
  • 2025年秦皇岛榻榻米定制公司推荐榜 - 2025年品牌推荐榜
  • 语音识别历史记录管理:轻松搜索与导出关键内容
  • 2026年靠谱的宁波刑事律师排行:陈群律师的专业推荐 - 2025年品牌推荐榜
  • 太流批了,语音转文字神器
  • OpenMV识别物体支持多目标追踪的安防模型:全面讲解
  • 热词列表格式详解:每行一个词汇提升识别命中率
  • 会议记录自动化系统原型演示视频发布
  • arm64和x64软浮点与硬浮点ABI差异详解
  • 分类讨论 3800, 3789
  • 并发用户数限制说明:免费版最多支持10个并发
  • ISSUE提交规范:请附带日志与复现步骤以便排查
  • 支持Chrome、Edge、Firefox:Fun-ASR跨浏览器兼容测试
  • 深入探讨Android ROM开发定制:从AOSP到LineageOS移植与Linux Rootfs适配
  • Kubernetes(一)——认识Kubernetes
  • 语音识别慢?教你正确配置GPU提升Fun-ASR运行速度
  • 用量统计面板:实时查看剩余Token数量
  • Pull Request审核流程:核心维护者每日定时合并
  • 实战案例解析:整流电路中二极管工作状态动态分析
  • 图解arm64-v8a汇编与链接过程核心要点
  • 模型卸载功能用途:节省资源用于其他深度学习任务
  • 语音活动检测(VAD)与Fun-ASR协同工作的最佳实践