Spring整合JDBC

这篇博客详细介绍了如何在Spring中整合JDBC,包括使用JDBC的六个步骤、数据库连接池C3P0、数据源管理以及Spring的JDBC模板类。此外,还探讨了Spring的声明式事务处理,包括事务管理器配置、事务通知和事务切面,以及事务管理策略,如异常回滚规则。最后提到了通过注解方式开启事务的使用场景。

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

回顾JDBC
java操作关系型数据的API。
导入相关数据库的驱动包后可以通过JDBC提供的接口来操作数据库。

实现JDBC的六个步骤
注册数据库驱动

获取数据库连接

获取传输器对象

传输sql执行获取结果集对象

遍历结果集获取信息

关闭资源

package cn.tedu.jdbc;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

/**

create database springdb;

use springdb;

create table stu(id int primary key auto_increment,name varchar(255),addr varchar(255));

insert into stu values (null,‘aaa’,‘bj’),(null,‘bbb’,‘sh’),(null,‘ccc’,‘gz’),(null,‘ddd’,‘sh’),(null,‘eee’,‘bj’);

*/

public class Demo01 {

public static void main(String[] args){

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

try {

//1.注册数据库驱动

Class.forName(“com.mysql.jdbc.Driver”);

//2.获取数据库连接

conn = DriverManager.getConnection(“jdbc:mysql:///springdb”,“root”,“root”);

//3.获取传输器

ps = conn.prepareStatement(“select * from stu where id < ?”);

ps.setInt(1, 4);

//4.执行sql获取结果集

rs = ps.executeQuery();

//5.遍历结果集获取结果

while(rs.next()){

String name = rs.getString(“name”);

System.out.println(name);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

//6.关闭资源

if(rs!=null){

try {

rs.close();

} catch (SQLException e) {

e.printStackTrace();

} finally{

rs = null;

}

}

if(ps!=null){

try {

ps.close();

} catch (SQLException e) {

e.printStackTrace();

} finally{

ps = null;

}

}

if(conn!=null){

try {

conn.close();

} catch (SQLException e) {

e.printStackTrace();

} finally{

conn = null;

}

}

}

}

}

数据库连接池(数据源)
C3P0连接池

package cn.tedu.jdbc;

import java.beans.PropertyVetoException;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**

create database springdb;

use springdb;

create table stu(id int primary key auto_increment,name varchar(255),addr varchar(255));

insert into stu values (null,‘aaa’,‘bj’),(null,‘bbb’,‘sh’),(null,‘ccc’,‘gz’),(null,‘ddd’,‘sh’),(null,‘eee’,‘bj’);

*/

public class Demo02 {

private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

static{

try {

dataSource.setDriverClass(“com.mysql.jdbc.Driver”);

dataSource.setJdbcUrl(“jdbc:mysql:///springdb”);

dataSource.setUser(“root”);

dataSource.setPassword(“root”);

} catch (PropertyVetoException e) {

e.printStackTrace();

throw new RuntimeException(e);

}

}

public static void main(String[] args){

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

try {

//1.2.从数据源中获取连接

conn = dataSource.getConnection();

//3.获取传输器

ps = conn.prepareStatement(“select * from stu where id < ?”);

ps.setInt(1, 4);

//4.执行sql获取结果集

rs = ps.executeQuery();

//5.遍历结果集获取结果

while(rs.next()){

String name = rs.getString(“name”);

System.out.println(name);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

//6.关闭资源

if(rs!=null){

try {

rs.close();

} catch (SQLException e) {

e.printStackTrace();

} finally{

rs = null;

}

}

if(ps!=null){

try {

ps.close();

} catch (SQLException e) {

e.printStackTrace();

} finally{

ps = null;

}

}

if(conn!=null){

try {

conn.close();

} catch (SQLException e) {

e.printStackTrace();

} finally{

conn = null;

}

}

}

}

}

Spring整合JDBC - 管理数据源
导入相关开发包
计算机生成了可选文字:aopalliance-1.O.Jar a ’ pe 戲 」 一 17 胃 刂 ar ; aspectJweaver-1.7.4.Jar
capo . g 12 r commons-logging-I.O.4.Jar mysql connector-Java-5.1.10-bin.Jar
spring-aop-3.2.3.RELEASE.Jar spring-aspects-3.2.3.RELEASE.Jar
spring-beans-3.2.3.RELEASE.Jar spring-context-3.2.3.RELEASE.Jar
spring-context-support-3.2.3.RELEASE.Ja spring-core-3.2.3.RELEASE.Jar
’ rl -ex ress10n-3.2.3.RELEASE.Jar spring-jdbc-3.2 3.RELEASEjar
-test-3.2.3.RELEASE. 3 r spring-tx-3.2.3.RELEASE.Jar

将数据源交于Spring管理
计算机生成了可选文字:dbc.propertles driverC1ass:com . mysql . jdbc . Driver
jdbcUr1:jdbc : mysql : //localhost : 3386/spring user:root password:root

计算机生成了可选文字:引 入 配 置 文 件 一 一 〉
(context:property-placeholder location:"cLasspath:/jdbc.properties
配 置 据 源 一 一 〉
(bean id: ’ ’ da 亡 0S0 rce class:"com/mchange.v2.c3pe. ComboPooL edDataSource ’ 〉
(property name:“driverCLass” value:" f d r i v e r C L a s s ) " X / p r o p e r t p ( p r o p e r t y n a m e : " j d b c U r L " v a l u e : " fdriverCLass)"X/propertp (property name:"jdbcUrL" value:" fdriverCLass)"X/propertp(propertyname:"jdbcUrL"value:"fjdbcUrL)"X/propertY)
(property name:“user” value:" f u s e r ) " x / p r o p e r t y ) ( p r o p e r t y n a m e : a s s 劂 o r 冒 " v a l u e : " fuser)"x/property) (property name: ass 劂 or 冒 " value:" fuser)"x/property)(propertyname:assor"value:"fpassword)"x/property) K/bean)

通过spring获取数据源,获取连接,操作数据库
package cn.tedu;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**

  • 通过spring管理数据源

*/

public class Demo01 {

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);

DataSource dataSource = (DataSource) context.getBean(“dataSource”);

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

try {

conn = dataSource.getConnection();

ps = conn.prepareStatement(“select * from user where id = ?”);

ps.setInt(1, 2);

rs = ps.executeQuery();

while(rs.next()) {

String name = rs.getString(“name”);

System.out.println(name);

}

} catch (SQLException e) {

e.printStackTrace();

}finally {

if(rs!=null) {

try {

rs.close();

} catch (SQLException e) {

e.printStackTrace();

} finally {

rs = null;

}

}

if(ps!=null) {

try {

ps.close();

} catch (SQLException e) {

e.printStackTrace();

} finally {

ps = null;

}

}

if(conn!=null) {

try {

conn.close();

} catch (SQLException e) {

e.printStackTrace();

} finally {

conn = null;

}

}

}

}

}

Spring整合JDBC - JDBC模板类
使用模版类能够极大的简化原有JDBC的编程过程。

在Spring中配置JDBC模板类
计算机生成了可选文字:
(bean id:“jdbcTempLate” class:"org/springframework.jdbc.core.JdbcTempLate
(property name: ’ ’ da 亡 0S0 rce ref:"dataSource"X/propertY) K/bean)

使用JDBC模板类实现增删改查
/**

  • 查询 - 查询一个SqlRowSet,自己处理结果

*/

@Test

public void test06() {

    JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");

    SqlRowSet rs = jdbcTemplate.queryForRowSet("select * from user");

    while(rs.next()) {

            String name = rs.getString("name");

            System.out.println(name);

    }

}

/**

  • 查询 - 查询一个Map,仅限于结果是一条记录时使用

*/

@Test

public void test05() {

    JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");

    Map<String, Object> map = jdbcTemplate.queryForMap("select * from user where id = 2");

    System.out.println(map);

}

/**

  • 查询 - 查询一个List

*/

@Test

public void test04() {

    JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");

    List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from user");

    System.out.println(list);

}

/**

  • 删除

*/

@Test

public void test03() {

    JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");

    jdbcTemplate.update("delete from user where id = ?" ,4);

}

/**

  • 修改

*/

@Test

public void test02() {

    JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");

    jdbcTemplate.update("update user set age = ? where id = ?",44,4);

}

/**

  • 新增

*/

@Test

public void test01() {

    JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");

    jdbcTemplate.update("insert into user values (?,?,?)",4,"ddd",33);

}

使用RowMapper封装bean
RowMapper接口定义了对象到列的映射关系,可以帮助我们在查询时自动封装bean。

/**

  • 查询 - 查询结果交给一个RowMapper处理,将结果封装为bean

*/

@Test

public void test08() {

    JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");

    User user = jdbcTemplate.queryForObject(

            "select * from user where id = ?"

            , new RowMapper<User>() {

                    @Override

                    public User mapRow(ResultSet rs, int index) throws SQLException {

                            User user = new User();

                            user.setId(rs.getInt("id"));

                            user.setName(rs.getString("name"));

                            user.setAge(rs.getInt("age"));

                            return user;

                    }

            }

            ,2);

    System.out.println(user);

}

/**

  • 查询 - 查询结果交给一个RowMapper处理,将结果封装为bean

*/

@Test

public void test07() {

    JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");

    List<User> user = jdbcTemplate.query(

            "select * from user where id >= ?"

            ,new RowMapper<User>() {

                    @Override

                    public User mapRow(ResultSet rs, int index) throws SQLException {

                            User user = new User();

                            user.setId(rs.getInt("id"));

                            user.setName(rs.getString("name"));

                            user.setAge(rs.getInt("age"));

                            return user;

                    }

            } ,

            2);

    System.out.println(user);

}

使用BeanPropertyRowMapper自动进行映射
BeanPropertyRowMapper内部可以使用指定类进行反射(内省)来获知类内部的属性信息,自动映射到表的列。

使用它一定要注意,类的属性名要和对应表的列名必须对应的上,否则属性无法自动映射。

BeanPropertyRowMapper底层通过反射(内省)来实现,相对于之前自己写的RowMapper效率比较低。

/**

  • 查询 - 查询结果交给一个BeanPropertyRowMapper处理,将结果封装为bean

  • BeanPropertyRowMapper是RowMapper的实现

  • 内部通过内省(反射)技术获取bean的所有bean属 性,对应获取查询结果中的同名列的值设置到bean中

  • 最终返回封装了结果的bean

  • BeanPropertyRowMapper用起来方便,但效率相对低一些。

*/

@Test

public void test09() {

JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean(“jdbcTemplate”);

User user = jdbcTemplate.queryForObject(“select * from user where id = ?”,new BeanPropertyRowMapper(User.class),2);

System.out.println(user);

}

Spring整合JDBC - 声明式事务处理
在之前的案例中,我们实现了自定义切面管理事务:

开发事务管理注解

开发事务管理切面

开发事务管理器TransactionManager

在事务管理器中通过ThreadLocal保证每个线程各自使用各自的Conn

事实上,SpringJDBC中提供了内置的事务处理机制,可以经过简单的配置完成事务管理,这称之为声明式事务处理。

创建项目,模拟MVC三层架构

在配置文件中导入相关约束
计算机生成了可选文字:K?xml version:“l.e” encoding:“UTF-8”?)
(beans xmlns:“http://www.springframework.org/schema/beans
xmlns : context: " h 亡 亡 : / / 鼽 . springframework . org/schema/context "
xmlns : aop: " h 亡 亡 : / / 鼽 . springframework . org/schema/aop
xmlns : tx: " h 亡 亡 : / / 鼽 . springframework . org/schema/tx”
xmlns : xsi: " h 亡 亡 : / / . . org/201/XMLSchema- instance
XSI : schemaLocation— . org/schema/beans
. org/schema/beans/spring-beans-3.2.xsd . org/schema/context
. org/schema/context/spring-context-3 . 2 . xsd . org/schema/aop
. org/schema/aop/spring-aop-3.2.xsd . org/schema/tx
. org/schema/tx/spring-tx-3.2.xsd h 亡 : / / 鼽 vw h 亡 : / / 鼽 vw h 亡 : / / 鼽 vw
h 亡 : / / 鼽 vw h 亡 : / / 鼽 vw h 亡 : / / 鼽 vw 向 亡 : / / 鼽 vw 向 亡 : / / 鼽 vw
. springframework . springframework . springframework . springframework
. springframework . springframework . springframework . springframework

配置事务管理器
计算机生成了可选文字:配 置 事 务 管 理 器 一 一 〉 (bean " 亡 亡 讠 on "
class: org.springframework.jdbc.datasource.DataSourceTransactionManager"
(property name: ’ ’ da 亡 0S0 rce ref:"dataSource"X/propertY) K/bean)

配置事务通知
计算机生成了可选文字:配 置 事 务 通 知 - (tx:advice id:"txAdvice
transaction—manager: " 亡 亡 讠 on ’ 〉 < t x : attributes)
’ ’ ad 冒 * ’ ’ propagation:“REQUIRED”/) (tx:method name: propagation:"REQUIRED’
(tx:method name:“deLete*” propagation: "REQUIRED ’ (tx:method name:“update*”
K/tx:attributes) K/tx:advice)

配置事务切面
计算机生成了可选文字:配 置 事 务 切 面 一 一 〉 GOP : config) Kaop:pointcut expression—
exec 亡 讠 on ( * Cn. . sprtng. service. * . * Kaop:advisor advice—ref:
’ ’ 亡 x 胃 L ’ 讠 ce ’ pointcut-ref:"pc’ / 〉 K/aop:confio

配置关系图
计算机生成了可选文字:配 置 据 源 一 一 〉
(bean id: " da 亡 0S0 C e " class:"com/mchange.v2.c3pe. ComboPooL edDataSource ’ 〉
“driverCLass” value:" f d r i v e r C L a s s ) " X / p r o p e r t p b c U r L " v a l u e : " fdriverCLass)"X/propertp bcUrL" value:" fdriverCLass)"X/propertpbcUrL"value:"fjdbcUrL)"X/propertY)
(property name:"use ’ value:"KaTeX parse error: Double superscript at position 27: …operty) ass ' '̲ value:"fpassword)"x/property)
mework.jdbc.d 0s0 rce . Da 亡 0S0 rce 丆 ransac 亡 讠 on anager " n (property nam
(property name: (property name: K/bean) 配 置 事 务 管 理 器 一 一 〉 (bean id:"transact
class: org.spr (property name: ’ ’ da 亡 0S0 K/bean) 配 置 事 务 通 知 -
ref: ’ ’ da 亡 0S0 rce "X/property) (tx:advice id:"txAdvice < t x : attributes)
(tx:method na e: (tx:method nam (tx:method name K/tx:attributes) K/tx:advice)
配 置 事 务 切 面 一 一 〉 GOP : config) transaction—manager: " 亡 亡 讠 on ’ 〉
’ ’ ad 冒 * ’ ’ pro agation:"REQUIRED’ 7 〉 ropagation: "REQUIRED ’ “deLete*”
ropagation: "REQUIRED ’ " da 亡 e * " xecution(* Cn. . service. * . *
’ ’ 亡 x 胃 vice pointcut-ref:‘pc’ / 〉 Kaop:pointcut expression— 卜 d : 乍 0 ’ 7
Kaop:advisor advice—ref: K/aop:confio

事务管理策略
异常的种类:

java.lang.Throwable

|-Exception

|-Runtime Exception

|-其他Exception

|-Error

SpringJDBC内置的事务策略,只在底层抛出的异常是运行时异常时,才会回滚,其他异常不回滚,留给用户手动处理。

也可以在配置中指定在原有规则的基础上,哪些异常额外的回滚或不回滚:

计算机生成了可选文字:配 置 事 务 通 知 - (tx:advice id:"txAdvice
transaction—manager: " 亡 亡 讠 on ’ 〉 < t x : attributes)
’ ’ ad * ’ ’ propagation:“REQUIRED’ (tx:method name: ’ 0 . .SQLException”
rollback-for— no-rollback-for:"Java. Lang . 胃 r 讠 亡 hme 亡 讠 CExce 亡 讠 on " / 〉
propagation:"REQUIRED’ (tx:method name:“deLete*” 7 〉 ktx:method name:“update*”
propagation:“REQUIRED” K/tx:attributes)

配置成如下形式,可以实现任意异常都自动回滚:

计算机生成了可选文字:配 置 事 务 通 知 - (tx:advice id:"txAdvice
transaction—manager: " 亡 亡 讠 on ’ 〉 < t x : attributes) ktx:method name:
(tx:method name:“deLete*” K/tx:attributes) K/tx:advice) ro a ation:“RE
rollback-for— ’ 口 at ’ 0 . L ang . ThrowabLe propagation
(tx:method name:“update*” propagation UTRED” "REQUIRED " "REQUIRED "

注意:如果在一个业务逻辑中,需要有多步不同表的操作,此时应该在service层中完成对不同表的操作,以此保证多步操作处在同一个事务中,切勿将业务代码在web层中调用不同Service来实现,虽然正常情况下也可以完成功能,但一旦出问题,很可能只有部分操作被回滚,数据出问题。
出现这种问题,不是事务管理机制的问题,而是开发者将业务逻辑错误的在web层中进行了实现,所以切记,web层只负责和用户交互和业务逻辑的调用,不进行任何业务逻辑的处理,任何业务逻辑都在service层中进行。

声明式事务处理 - 注解方式
开启事务注解配置
计算机生成了可选文字:‘bean id = ” 冒 0 亡 aSource ” class:"com/mchange. v2 . ComboPooLed
’ r 讠 verCL ass ” value:"com/mysqL.jdbc.Drive (property name:
(property name:"jdbcUrL ” value:"jdbc:mysql:///springdb’$
‘user ” value:"root’ x/property» (property name:
(property name: ass 射 or 冒 " value:"root’ x/property» K/bear»

计算机生成了可选文字:<!一酉己置事务管理器一><beanid二”t广ansact觉on河anage尸”
class二‘,o尸g.sp广觉n扩广a功e闪。广九.jdbc<propertyname二’‘datasou广ce’,
.datasou尸ce.Datasou广ceT尸ansact觉on河anage广”>ref二‘,datasou广ce">

计算机生成了可选文字:廾 启 spring 的 注 解 方 式 配 置 事 务 - (tx:annotation—driven/)

在方法上通过注解开启事务
即可以标注在接口上,也可以标注在实现类上,理论上应该表在接口上,实现面向接口编程,但实际开发中为了方便也有人写在实现类上。

计算机生成了可选文字:Transactional public void addUser(User user) throws SQLException;

计算机生成了可选文字:averride @TransactionaI
public void addlJser(User user) throws SQLException { userDao . addUser(user) ;
int i I/e; //throw new SQLException();

也可以在类上使用此接口,此时类中所有方法都会有事务

计算机生成了可选文字:Transactional
public class UserService 工 mp1 implements UserServ1ce {

当在类上开启了事务后,可以此类的方法中使用如下方式,控制某个方法没有事务

计算机生成了可选文字:averride @Transactiona1(propagation:Propagation . 0 丆 SUPPORTED)
public void addUser(User user) throws SQLException userDao . addUser(user) ;
int i I/e; //throw new SQLException();

通过注解控制事务时,和配置文件方式控制事务相同的是,默认只有运行时异常会回滚,非运行异常不回滚,此时可以通过如下注解选项额外配置 哪些异常需要回滚,哪些不需要。

计算机生成了可选文字:@Transactiona1 ac or: rowa e . C a S S n 0 0 ac
public void addUser(User user) throws SQLException userDao . addUser(user) ;
//int i 1/8; throw new SQLException(); or: xcep Ion.c a S S

扩展案例
**扩展案例:缓存的使用 - 通过AOP实现为查询操作增加缓存机制

@Component

@Aspect

public class CatchAspect {

/**

  • 使用Map实现的缓存

  •     只会增加不会减少
    
  •     没有超时时间,不管过了多久都使用历史缓存
    
  •     没有存储优化
    

*/

private Map<Integer,User> map = new HashMap<Integer,User>();

@Around(“execution(* cn.tedu.spring.service.*.queryUser(…))”)

public User around(ProceedingJoinPoint jp) throws Throwable{

int i = (Integer) jp.getArgs()[0];

if(map.containsKey(i)){

System.out.println(“使用缓存查询。。。”);

//有缓存

return map.get(i);

}else{

System.out.println(“使用数据库查询。。。”);

//没缓存

User user = (User) jp.proceed();

map.put(user.getId(), user);

return user;

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值