Spring JDBC模板及事务管理

Spring JDBC模板

在使用普通的 JDBC 数据库时,需要写代码来处理异常,打开和关闭数据库连接等。但Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常,处理事务,到最后关闭连接。当从数据库中获取数据时,你所需要做的只是定义连接参数,指定要执行的 SQL 语句。

Spring框架提供了JDBC模板模式,即JdbcTemplate,简化许多代码,但在实际应用中jdbcTemplate并不常用。更多的时候,用的是Hibernate框架和MyBatis框架进行数据库编程。

1.1 JdbcTemplate 配置

  1. 在配置文件中需要引入上下文约束、aop约束、tx约束。
  2. 注册DriverManagerDataSource类作为连接池(spring默认的连接池,如果使用其他连接池可以修改注册的bean类);在属性中设置驱动类,数据库连接地址,用户名和密码(其他连接设置可参照JDBC)。
  3. 注册springJDBC的核心类JdbcTemplate,在使用中就可以实例化该bean创建数据库连接。
<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <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/selection_course"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
</beans>
复制代码

1.2 JdbcTemplate 常用方法

public int update(String sql, Object[] args)

返回值为int类型,代表影响的行数,sql是需要执行的SQL语句,如果需要提供参数可以使用?来占位,args是需要向SQL语句中传递的参数,以数组的形式传递。

String insertSql = "insert into user values(null,?,?)";
Object param1[] = {"chenheng1""男"};
jdbcTemplate.update(sql, param1);
复制代码

对数据表进行查询操作,rowMapper将结果集映射到用户自定义的类中,实际应用中常用BeanPropertyRowMapper,自定义类中的属性要与数据表的字段对应。

1.3 JdbcTemplate实例

public List<T> query (String sql, RowMapper<T> rowMapper, Object args[])

  1. 创建数据库及对应的po类

111111.png
MyUser.java需要对应的setter和getter,否则在select的时候无法正确赋值

package com.pojo;

public class MyUser {
	private Integer uid;
	private String uname;
	private String usex;

	public Integer getUid() {
		return uid;
	}

	public void setUid(Integer uid) {
		this.uid = uid;
	}

	public String getUname() {
		return uname;
	}

	public void setUname(String uname) {
		this.uname = uname;
	}

	public String getUsex() {
		return usex;
	}

	public void setUsex(String usex) {
		this.usex = usex;
	}
	
	public String toString() {
		return "myUser [uid=" + uid +", uname=" + uname + ", usex=" + usex + "]";
	}
}
复制代码
  1. 在XML中配置bean JdbcTemplate,dataSource是数据池类,需要配置的属性包括驱动类,URL,用户名和用户密码。将dataSource注入JdbcTemplate中。
<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <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/sql_springtest?characterEncoding=utf8&amp;useSSL=false" />
        <property name="username" value="root"/>
        <property name="password" value="20011017lh"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
</beans>
复制代码
  1. 测试类

TestJdbc.java,在测试代码中使用了@Test单元测试,这个测试默认是不支持注解注入的,需要加上

@RunWith(SpringJUnit4ClassRunner.class)//SpringJUnit支持,由此引入Spring-Test框架支持! 
@ContextConfiguration(locations="classpath:applicationContext.xml")//加载
复制代码
package com.dao;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.pojo.MyUser;

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)//SpringJUnit支持,由此引入Spring-Test框架支持! 
@ContextConfiguration(locations="classpath:applicationContext.xml")//加载applicationContext.xml
@Repository("testJdbc")
public class TestJdbc {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Test
	public void select() {
		String sql = "select * from user where uid = ? and uname = ?";
		RowMapper<MyUser> rowMapper = new BeanPropertyRowMapper<MyUser>(MyUser.class);
		Object[] ids = new Object[]{2,"王小虎"};
		List<MyUser> ans = jdbcTemplate.query(sql,rowMapper,ids);
		System.out.println(Arrays.toString(ans.toArray()));
	}
	
	@Test
	public void update() {
		String sql = "insert into user values(null, ?, ?)";
		Object[] params = {"王中虎", "男"};
		int ans = jdbcTemplate.update(sql,params);
		System.out.println(ans);
	}
}
复制代码

Spring 事务管理 – 编程式

事务:必须连续执行的一系列操作,Spring中有多种方式对事物进行管理。

2.1 事务管理器

在代码中显式调用beginTransaction()commit()rollback()等事务处理相关的方法,就是编程式事务管理,过编程的方式来进行事务处理。当只有少数事务操作时,编程式事务管理才比较合适。

  • PlatformTransactionManager

    Spring的事务基础结构的中心接口,常用实现类

    • DataSourceTransactionManager:使用JDBC管理事务
    • HibernateTransactionManager:使用Hibernate管理事务    -
  • TransactionDefinition

Spring的事务定义,用于定义事务相关的信息。例如,隔离级别、传播行为、是否只读、超时信息等

  • TransactionStatus

用于记录事务管理的过程中,事务的状态信息

Spring进行事务管理时,PlatformTransactionManager根据TransactionDefinition进行事务的管理,这个过程中产生各种状态信息将记录到TransactionStatus中

实例:

  1. 在XML中配置DataSourceTransactionManager的bean,他与JDBC模板配合使用,JDBC模板进行数据库操作,DataSourceTransactionManager对事务进行管理
<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    <!-- 配置数据源 -->
    <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/sql_springtest?characterEncoding=utf8&amp;useSSL=false" />
        <property name="username" value="root"/>
        <property name="password" value="20011017lh"/>
    </bean>
    
    <!-- 配置JDBC模板 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 配置事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>
复制代码
  1. 测试使用事务管理器进行事务管理
package com.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
@Repository("codeTransaction")
public class CodeTransaction {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Autowired
	private DataSourceTransactionManager txManager;
	
	@Test
	public void test()  {
		TransactionDefinition tf = new DefaultTransactionDefinition();  // 默认事务定义,如隔离级别、传播行为等
		TransactionStatus ts = txManager.getTransaction(tf);
		String message = "执行成功,没有事务回滚";
		try {
			String sql1 = " insert into user values(?,?,?) ";
			Object param[] = { 1, "陈恒", "男" };
			jdbcTemplate.update(sql1, param);  // 添加一条数据
			jdbcTemplate.update(sql1, param);  // 添加相同的一条数据,使主键重复
			// 提交事务
			txManager.commit(ts);
		} catch (Exception e) {
			txManager.rollback(ts); //由事务管理器进行回滚
			message = "事务回滚";
			//e.printStackTrace();
		}
		System.out.println(message);
	}
}
复制代码

2.2 事务管理器模板

不管是JDBC模板还是事务管理模板都是为了简化代码,使用模板方法设计模式对原始JDBC/事务管理方式进行封装。

  • TransactionTemplate

TransactionTemplate的execute()方法有一个TransactionCallback接口类型的参数,该接口中定义了一个doInTransaction()方法,通常以匿名内部类的方式实现TransactionCallback 接口,并在其doInTransaction()方法中书写业务逻辑代码。

这里可以使用默认的事务提交和回滚规则,在业务代码中不需要显式调用任何事务处理的API。doInTransaction()方法有一个TransactionStatus类型的参数,可以在方法的任何位置调用该参数的setRollbackOnly()方法将事务标识为回滚,以执行事务回滚。

根据默认规则,如果在执行回调方法的过程中抛出了未检查异常,或者显式调用了setRollbackOnly()方法,则回滚事务;如果事务执行完成或者抛出了checked类型的异常,则提交事务。

基本结构

public Object getObject(String str) {
        /*
         *  执行带有返回值<Object>的事务管理
         */
        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {

                try {
                      ...
                    //.......   业务代码
                    return new Object();
                } catch (Exception e) {
                    //回滚
                    transactionStatus.setRollbackOnly();
                    return null;
                }
            }
        });
}
复制代码

实例:

  1. 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"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    <!-- 配置数据源 -->
    <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/sql_springtest?characterEncoding=utf8&amp;useSSL=false" />
        <property name="username" value="root"/>
        <property name="password" value="20011017lh"/>
    </bean>
    
    <!-- 配置JDBC模板 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 配置事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
	
    <!-- 为事务管理器txManager创建transactionTemplate -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="txManager" />
    </bean>
</beans>
复制代码
  1. 测试
package com.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")

@Repository("transactionTemplateDao")
public class TransactionTemplateDao {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Autowired
	private TransactionTemplate transactionTemplate;
	String message = "";
	
	@Test
	public void test() {
		// 以匿名内部类的方式实现TransactionCallback 接口,使用默认的事务提交和回滚规则,在业务代码中不需要显式调用任何事务处理的API
		transactionTemplate.execute(new TransactionCallback<Object>() {
			@Override
			public Object doInTransaction(TransactionStatus ts) {
				String sql1 = " insert into user values(?,?,?) ";
				Object param[] = { 2, "陈恒", "男" };
				try {
					jdbcTemplate.update(sql1, param);
					jdbcTemplate.update(sql1, param);
					message = "执行成功,没有事务回滚";
				} catch (Exception e) {
					message = "主键重复,事务回滚";
					//e.printStackTrace();
					ts.setRollbackOnly();
				}
				return message;
			}
		});
		System.out.println(message);
	}
}
复制代码

Spring 事务管理 – 声明式

Spring的声明式事务管理,是通过AOP技术实现的事务管理,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务管理最大的优点是不需要通过编程的方式管理事务,因而不需要在业务逻辑代码中掺杂事务处理的代码,只需相关的事务规则声明,便可以将事务规则应用到业务逻辑中。通常情况下,在开发中使用声明式事务处理,不仅因为其简单,更主要是因为这样使得纯业务代码不被污染,极大方便后期的代码维护。

采用AOP实现,所以可以使用XML和注解两种方式来实现

3.1 基于XML

基于XML方式的声明式事务管理是通过在配置文件中配置事务规则的相关声明来实现的。

Spring框架提供了tx命名空间来配置事务,<tx:advice>元素来配置事务的通知。

配置<tx:advice>元素需指定id和transaction-manager属性,id属性是配置文件的唯一标识,transaction-manager属性指定事务管理器。

<tx:attributes>子元素可配置多个<tx:method>子元素指定执行事务的细节。

<tx:advice>元素配置了事务的增强处理后,就可以通过编写AOP配置,让Spring自动对目标对象生成代理。

完整项目地址

创建了Dao、Service和Controller三层,,具体实现步骤如下:

  1. 在XML中配置JDBC模板连接数据库
<!-- 配置数据源 -->
<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/sql_springtest?characterEncoding=utf8&amp;useSSL=false" />
    <property name="username" value="root"/>
    <property name="password" value="20011017lh"/>
</bean>

<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>
复制代码
  1. 建立Dao层并添加注解,Dao层通过jdbc模板操作数据库
package com.dao;
@Repository("testDao")
public class TestDaoImpl implements TestDao{
	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Override
	public int save(String sql, Object[] param) {
		return jdbcTemplate.update(sql,param);
	}
	@Override
	public int delete(String sql, Object[] param) {
		return jdbcTemplate.update(sql,param);
	}
}
复制代码
  1. 建立Service层并添加注解,Service层通过Dao层提供的方法向控制层提供save和delete方法
package com.service;
import com.dao.TestDao;
@Service("testService")
public class TestServiceImpl implements TestService{
	@Autowired
	private TestDao testDao;
	@Override
	public int save(String sql, Object[] param) {
		return testDao.save(sql, param);
	}
	@Override
	public int delete(String sql, Object[] param) {
		return testDao.delete(sql, param);
	}
}
复制代码
  1. 建立Controller层并添加注解(这里多了个注解@Transactional),通过服务层提供的方法,编写test方法测试一个由三个操作组成的事务
package com.controller;
import com.service.TestService;
@Controller("statementController")
public class StatementController {
	@Autowired
	private TestService testService;
	public String test() {
		int i=0;
		String message = "";
		String deleteSql ="delete from user where id = 2";
		String saveSql = "insert into user values(?,?,?)";
		Object param[] = {1222,"chenheng1","男"};
		try{
			i=testService.delete(deleteSql, null);
			i=testService.save(saveSql, param);
			i=testService.save(saveSql, param);
		}catch(Exception e){
			message = "主键重复,事务回滚!"+e.getClass()+" ,i = "+i;
			//e.printStackTrace();
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		}
		return message;
	}
}
复制代码
  1. 在XML中添加注解扫描
<context:component-scan base-package="com"/>
复制代码
  1. 在XML中添加事务管理为事务管理器创建切面类通过AOP将切面类与切入点相连
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 编写通知声明事务-->
<tx:advice id="myAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- * 表示任意方法 -->
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>


<!-- 编写AOP,让Spring自动对目标对象生成代理,需要使用AspectJ的表达式 -->
<aop:config>
    <!-- 定义切入点 对控制层的所有类的所有方法,都执行切入-->
    <aop:pointcut id="txPointCut" expression="execution(* com.controller.*.*())"/>
    <!-- 切面:将切入点与通知关联 -->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="txPointCut"/>
</aop:config>
复制代码

3.2 基于注解

@Transactional作用于接口、接口方法、类以及类方法上,但是Spring小组建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。

当作用于类上时,该类的所有public方法将都具有该类型的事务属性,如果在方法级别使用该注解将覆盖类级别的定义。

示例也是建立Dao,Service,Controller,有两方面不同

  1. XML配置文件中开启扫描注解,配置JDBC模板,配置事务管理器,开启注解事务
<!-- 指定需要扫描的包(包括子包),使注解生效 -->
<context:component-scan base-package="com"/>

<!-- 配置数据源 -->
<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/sql_springtest?characterEncoding=utf8&amp;useSSL=false" />
    <property name="username" value="root"/>
    <property name="password" value="20011017lh"/>
</bean>

<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

    <!-- 编写通知声明事务,即切面类 -->
<tx:advice id="myAdvice" transaction-manager="txManager">
    <tx:attributes>
            <!-- * 表示任意方法 -->
            <tx:method name="*" />
    </tx:attributes>
</tx:advice>

<!-- 为事务管理器注册注解驱动 -->
<tx:annotation-driven transaction-manager="txManager" />
复制代码
  1. 在Spring MVC中,通常通过Service层进行事务管理,因此需要为Service层添加@Transactional注解。
@Service("testService")
@Transactional
//加上注解@Transactional,就可以指定这个类需要受Spring的事务管理
//注意@Transactional只能针对public属性范围内的方法添加
public class TestServiceImpl implements TestService{
    @Autowired
    private TestDao testDao;
    @Override
    public int save(String sql, Object[] param) {
        return testDao.save(sql, param);
    }
    @Override
    public int delete(String sql, Object[] param) {
        return testDao.delete(sql, param);
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享