Spring 中的 JDBC 事务

Spring 对 JDBC 的支持

JdbcTemplate 简介

•为了使 JDBC 更加易于使用, Spring 在 JDBC API 上定义了一个抽象层, 以此建立一个 JDBC 存取框架.

•作为 Spring JDBC 框架的核心, JDBC 模板的设计目的是为不同类型的 JDBC 操作提供模板方法. 每个模板方法都能控制整个过程, 并允许覆盖过程中的特定任务. 通过这种方式, 可以在尽可能保留灵活性的情况下, 将数据库存取的工作量降到最低.

使用 JdbcTemplate 更新数据库

 1     <!-- 导入资源文件 -->
 2     <context:property-placeholder location="classpath:db.properties"/>
 3
 4     <!-- 配置 C3P0 数据源 -->
 5     <bean id="dataSource"
 6         class="com.mchange.v2.c3p0.ComboPooledDataSource">
 7         <property name="user" value="${jdbc.user}"></property>
 8         <property name="password" value="${jdbc.password}"></property>
 9         <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
10         <property name="driverClass" value="${jdbc.driverClass}"></property>
11
12         <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
13         <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
14     </bean>
15
16     <!-- 配置 Spirng 的 JdbcTemplate -->
17     <bean id="jdbcTemplate"
18         class="org.springframework.jdbc.core.JdbcTemplate">
19         <property name="dataSource" ref="dataSource"></property>
20     </bean>

applicationContext.xml

 1     /**
 2      * 获取单个列的值, 或做统计查询
 3      * 使用 queryForObject(String sql, Class<Long> requiredType)
 4      */
 5     @Test
 6     public void testQueryForObject2(){
 7         String sql = "SELECT count(id) FROM employees";
 8         long count = jdbcTemplate.queryForObject(sql, Long.class);
 9
10         System.out.println(count);
11     }
12
13     /**
14      * 查到实体类的集合
15      * 注意调用的不是 queryForList 方法
16      */
17     @Test
18     public void testQueryForList(){
19         String sql = "SELECT id, last_name lastName, email FROM employees WHERE id > ?";
20         RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
21         List<Employee> employees = jdbcTemplate.query(sql, rowMapper,5);
22
23         System.out.println(employees);
24     }
25
26     /**
27      * 从数据库中获取一条记录, 实际得到对应的一个对象
28      * 注意不是调用 queryForObject(String sql, Class<Employee> requiredType, Object... args) 方法!
29      * 而需要调用 queryForObject(String sql, RowMapper<Employee> rowMapper, Object... args)
30      * 1. 其中的 RowMapper 指定如何去映射结果集的行, 常用的实现类为 BeanPropertyRowMapper
31      * 2. 使用 SQL 中列的别名完成列名和类的属性名的映射. 例如 last_name lastName
32      * 3. 不支持级联属性. JdbcTemplate 到底是一个 JDBC 的小工具, 而不是 ORM 框架
33      */
34     @Test
35     public void testQueryForObject(){
36         String sql = "SELECT id, last_name lastName, email, dept_id as \"department.id\" FROM employees WHERE id = ?";
37         RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
38         Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
39
40         System.out.println(employee);
41     }
42
43     /**
44      * 执行批量更新: 批量的 INSERT, UPDATE, DELETE
45      * 最后一个参数是 Object[] 的 List 类型: 因为修改一条记录需要一个 Object 的数组, 那么多条不就需要多个 Object 的数组吗
46      */
47     @Test
48     public void testBatchUpdate(){
49         String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(?,?,?)";
50
51         List<Object[]> batchArgs = new ArrayList<>();
52
53         batchArgs.add(new Object[]{"AA", "[email protected]", 1});
54         batchArgs.add(new Object[]{"BB", "[email protected]", 2});
55         batchArgs.add(new Object[]{"CC", "[email protected]", 3});
56         batchArgs.add(new Object[]{"DD", "[email protected]", 3});
57         batchArgs.add(new Object[]{"EE", "[email protected]", 2});
58
59         jdbcTemplate.batchUpdate(sql, batchArgs);
60     }
61
62     /**
63      * 执行 INSERT, UPDATE, DELETE
64      */
65     @Test
66     public void testUpdate(){
67         String sql = "UPDATE employees SET last_name = ? WHERE id = ?";
68         jdbcTemplate.update(sql, "Jack", 5);
69     }

JDBCTest

简化 JDBC 模板查询

•每次使用都创建一个 JdbcTemplate 的新实例, 这种做法效率很低下.

•JdbcTemplate 类被设计成为线程安全的, 所以可以再 IOC 容器中声明它的单个实例, 并将这个实例注入到所有的 DAO 实例中.

 1 @Repository
 2 public class EmployeeDao {
 3
 4     @Autowired
 5     private JdbcTemplate jdbcTemplate;
 6
 7     public Employee get(Integer id){
 8         String sql = "SELECT id, last_name lastName, email FROM employees WHERE id = ?";
 9         RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
10         Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, id);
11
12         return employee;
13     }
14 }

EmployeeDao

•Spring JDBC 框架还提供了一个 JdbcDaoSupport 类来简化 DAO 实现. 该类声明了 jdbcTemplate 属性, 它可以从 IOC 容器中注入, 或者自动从数据源中创建.

 1 @Repository
 2 public class DepartmentDao extends JdbcDaoSupport{
 3
 4     @Autowired
 5     public void setDataSource2(DataSource dataSource){
 6         setDataSource(dataSource);
 7     }
 8
 9     public Department get(Integer id){
10         String sql = "SELECT id, dept_name name FROM departments WHERE id = ?";
11         RowMapper<Department> rowMapper = new BeanPropertyRowMapper<>(Department.class);
12         return getJdbcTemplate().queryForObject(sql, rowMapper, id);
13     }
14
15 }

DepartmentDao

在 JDBC 模板中使用具名参数

•在 Spring JDBC 框架中, 绑定 SQL 参数的另一种选择是使用具名参数(named parameter).

•具名参数: SQL 按名称(以冒号开头)而不是按位置进行指定. 具名参数更易于维护, 也提升了可读性. 具名参数由框架类在运行时用占位符取代

•具名参数只在 NamedParameterJdbcTemplate 中得到支持

	<!-- 配置 NamedParameterJdbcTemplate, 该对象可以使用具名参数, 其没有无参数的构造器, 所以必须为其构造器指定参数 -->
	<bean id="namedParameterJdbcTemplate"
		class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
		<constructor-arg ref="dataSource"></constructor-arg>
	</bean>

 1     /**
 2      * 使用具名参数时, 可以使用 update(String sql, SqlParameterSource paramSource) 方法进行更新操作
 3      * 1. SQL 语句中的参数名和类的属性一致!
 4      * 2. 使用 SqlParameterSource 的 BeanPropertySqlParameterSource 实现类作为参数.
 5      */
 6     @Test
 7     public void testNamedParameterJdbcTemplate2(){
 8         String sql = "INSERT INTO employees(last_name, email, dept_id) "
 9                 + "VALUES(:lastName,:email,:dpetId)";
10
11         Employee employee = new Employee();
12         employee.setLastName("XYZ");
13         employee.setEmail("[email protected]");
14         employee.setDpetId(3);
15
16         SqlParameterSource paramSource = new BeanPropertySqlParameterSource(employee);
17         namedParameterJdbcTemplate.update(sql, paramSource);
18     }
19
20     /**
21      * 可以为参数起名字.
22      * 1. 好处: 若有多个参数, 则不用再去对应位置, 直接对应参数名, 便于维护
23      * 2. 缺点: 较为麻烦.
24      */
25     @Test
26     public void testNamedParameterJdbcTemplate(){
27         String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(:ln,:email,:deptid)";
28
29         Map<String, Object> paramMap = new HashMap<>();
30         paramMap.put("ln", "FF");
31         paramMap.put("email", "[email protected]");
32         paramMap.put("deptid", 2);
33
34         namedParameterJdbcTemplate.update(sql, paramMap);
35     }

TestNamedParameterJdbctemplate

Spring  中的事务管理

•作为企业级应用程序框架, Spring 在不同的事务管理 API 之上定义了一个抽象层. 而应用程序开发人员不必了解底层的事务管理 API, 就可以使用 Spring 的事务管理机制.

•Spring 既支持编程式事务管理, 也支持声明式的事务管理.

•编程式事务管理: 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚. 在编程式管理事务时, 必须在每个事务操作中包含额外的事务管理代码.

•声明式事务管理: 大多数情况下比编程式事务管理更好用. 它将事务管理代码从业务方法中分离出来, 以声明的方式来实现事务管理. 事务管理作为一种横切关注点, 可以通过 AOP 方法模块化. Spring 通过 Spring AOP 框架支持声明式事务管理.

用事务通知声明式地管理事务

•事务管理是一种横切关注点

•为了在 Spring 2.x 中启用声明式事务管理, 可以通过 tx Schema 中定义的 <tx:advice> 元素声明事务通知, 为此必须事先将这个 Schema 定义添加到 <beans> 根元素中去.

•声明了事务通知后, 就需要将它与切入点关联起来. 由于事务通知是在 <aop:config> 元素外部声明的, 所以它无法直接与切入点产生关联. 所以必须在 <aop:config> 元素中声明一个增强器通知与切入点关联起来.

 1     <!-- 配置 bean -->
 2     <bean id="bookShopDao" class="com.atguigu.spring.tx.xml.BookShopDaoImpl">
 3         <property name="jdbcTemplate" ref="jdbcTemplate"></property>
 4     </bean>
 5
 6     <bean id="bookShopService" class="com.atguigu.spring.tx.xml.service.impl.BookShopServiceImpl">
 7         <property name="bookShopDao" ref="bookShopDao"></property>
 8     </bean>
 9
10     <bean id="cashier" class="com.atguigu.spring.tx.xml.service.impl.CashierImpl">
11         <property name="bookShopService" ref="bookShopService"></property>
12     </bean>
13
14     <!-- 1. 配置事务管理器 -->
15     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
16         <property name="dataSource" ref="dataSource"></property>
17     </bean>
18
19     <!-- 2. 配置事务属性 -->
20     <tx:advice id="txAdvice" transaction-manager="transactionManager">
21         <tx:attributes>
22             <!-- 根据方法名指定事务的属性 -->
23             <tx:method name="purchase" propagation="REQUIRES_NEW"/>
24             <tx:method name="get*" read-only="true"/>
25             <tx:method name="find*" read-only="true"/>
26             <tx:method name="*"/>
27         </tx:attributes>
28     </tx:advice>
29
30     <!-- 3. 配置事务切入点, 以及把事务切入点和事务属性关联起来 -->
31     <aop:config>
32         <aop:pointcut expression="execution(* com.atguigu.spring.tx.xml.service.*.*(..))"
33             id="txPointCut"/>
34         <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
35     </aop:config>

config

用 @Transactional 注解声明式地管理事务

•为了将方法定义为支持事务处理的, 可以为方法添加 @Transactional 注解. 根据 Spring AOP 基于代理机制, 只能标注公有方法.

•可以在方法或者类级别上添加 @Transactional 注解. 当把这个注解应用到类上时, 这个类中的所有公共方法都会被定义成支持事务处理的.

•在 Bean 配置文件中只需要启用 <tx:annotation-driven> 元素, 并为之指定事务管理器就可以了.

•如果事务处理器的名称是 transactionManager, 就可以在<tx:annotation-driven> 元素中省略 transaction-manager 属性. 这个元素会自动检测该名称的事务处理器.

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

	<!-- 启用事务注解 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
	//添加事务注解
	//1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
	//如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
	//REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起.
	//2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
	//3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
	//属性进行设置. 通常情况下去默认值即可.
	//4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据,
	//这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true
	//5.使用 timeout 指定强制回滚之前事务可以占用的时间.
	@Transactional(propagation=Propagation.REQUIRES_NEW,
			isolation=Isolation.READ_COMMITTED,
			readOnly=false,
			timeout=3)
	@Override
	public void purchase(String username, String isbn) {
		//1. 获取书的单价
		int price = bookShopDao.findBookPriceByIsbn(isbn);
		//2. 更新数的库存
		bookShopDao.updateBookStock(isbn);
		//3. 更新用户余额
		bookShopDao.updateUserAccount(username, price);
	}
}

事务传播属性

•当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播. 例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行.

•事务的传播行为可以由传播属性指定. Spring 定义了 7  种类传播行为.第一和第二种最常用.

  

时间: 2024-10-09 21:06:37

Spring 中的 JDBC 事务的相关文章

Spring中的Jdbc事务管理

Spring提供了对事务的声明式事务管理,只需要在配置文件中做一些配置,即可把操作纳入到事务管理当中,解除了和代码的耦合. Spring声明式事务管理,核心实现就是基于Aop. Spring声明式事务管理是粗粒度的事务控制,只能给整个方法应用事务,不可以对方法的某几行应用事务. Spring声明式事务管理器类: Jdbc技术:DataSourceTransactionManager Hibernate技术:HibernateTransactionManager 1.xml方式声明事务 引入tx名

在Spring中基于JDBC进行数据访问时如何控制超时

超时分类 超时根据作用域可做如下层级划分: Transaction Timeout > Statement Timeout > JDBC Driver Socket Timeout Transaction Timeout指一组SQL操作执行时应在设定的时间内完成(提交或回滚),否则将引发超时.它的值应大于 N(语句数) * Statement Timeout Statement Timeout指完成单条SQL语句执行的最大允许时间.它的值应小于JDBC Driver Socket Timeou

Spring学习(四)spring中使用jdbc

spring dao层中对jdbc进行了封装,使用模板模式的设计模式,通过ioc被动注入的方式将jdbcTemplate这个模板类注入到数据对象中,进行数据库操作. 我们要在一个类中进行CRUD操作,首先要将jdbcTemplate这个模板类注入到数据对象类中,然后将DataSource这个类注入到jdbcTemplate,获取数据源. 这样数据对象类就可以通过jdbcTemplate类中的方法进行数据库操作了. 注意:这里需要导如spring jdbc的两个包和数据库驱动包 容器配置如下: <

Spring框架笔记(二十四)——Spring中的JDBC的两种使用方式

为了使 JDBC 更加易于使用, Spring 在 JDBC API 上定义了一个抽象层, 以此建立一个 JDBC 存取框架. 作为 Spring JDBC 框架的核心, JDBC 模板的设计目的是为不同类型的 JDBC 操作提供模板方法. 每个模板方法都能控制整个过程, 并允许覆盖过程中的特定任务. 通过这种方式, 可以在尽可能保留灵活性的情况下, 将数据库存取的工作量降到最低. 现在我们来介绍一下,各种CRUD可能用到的Spring JDBC的API: 使用 JdbcTemplate 更新数

Spring中配置Hibernate事务管理

<!-- transationManager --> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>

@Transactional注解在spring中的应用-事务

@Transactional spring 事务注解 默认遇到throw new RuntimeException("...");会回滚 需要捕获的throw new Exception("...");不会回滚 // 指定回滚 @Transactional(rollbackFor=Exception.class) public void methodName() { // 不会回滚 throw new Exception("..."); } //

spring中配置了事务,数据业务层捕获异常,事务配置不成功?

这个问题我是解决了.原来我对spring的事务切如原理不是特别了解. 解决方案如下: 原理:spring aop  异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过<tx:method name="upd*" propagation="REQUIRED" rollback-for="java.lang.Exceptio

spring中配置jdbc数据源

1.加入jdbc驱动器包,mysql-connector-java.jar 2.加入commons-dbcp.jar配置数据源 3.在classpath下新建文件jdbc.properties,配置jdbc数据源参数 jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc\:mysql\://localhost\:3306/baiyin jdbc.username=root jdbc.password=123456 4.在xml中导入数据

spring中编程式事务控制

step1:配置xml文件 1 <!-- 事务管理bean --> 2 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 3 <property name="dataSource" ref="dataSource" /> 4 </