Spring学习笔记四(声明式事务)

Spring的声明式事务

在web开发中,免不了要与数据库打交道,一旦与数据库联系上了就免不了要写事务,保证业务逻辑的正常运行。在以前写事务,我们需要自己通过connection开启事务setAutoCommit(false),然后正常则提交commit,异常就回滚rollback,通过事务能够保证数据的一致性,如果有许多业务都需要开启事务,这样就会出现许多重复的代码,使得代码臃肿,编写也比较繁琐,因此spring为我们提供了一个声明式事务,即我们不用再自己编写事务,只需要在配置文件中配置一事务管理器,然后通过Spring的aop对需要开启事务的业务进行动态插入,提高了代码的灵活性,降低了代码量。

JdbcTemplate的使用

在进行声明式事务的操作之前,需要先了解下spring是如何对数据库操作的,在spring中也对jdbc进行了封装,也就是jdbctemplate,其api的使用方式与dbutils的极其相似。
Spring学习笔记四(声明式事务)_第1张图片

要使用JdbcTemplate,除了spring的基本包和数据库连接池的包,还需要导入以下jar包:
spring-jdbc-4.2.4.RELEASE.jar
spring-tx-4.2.4.RELEASE.jar

  • 配置文件示例



        
        
        
        
        
        
        	
        	
        	
        	
        
        
        
        
        	
         

        
        
        	
        
        
        
        
        	
        

  • Dao层示例
package com.wzm.dao;

import java.util.List;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import com.wzm.entity.Account;

/*
 * Dao层
 */
public class AccountDao{
	
	//定义JdbcTemplate对象和其set方法,然后使用依赖注入获取对象
	private JdbcTemplate jdbcTemplate;
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	//保存数据
	public void save() {
		String sql = "insert into account values(null,?,?)";
		//增删改的使用方式与dbutils类似都是使用update方法,
		//第一个参数为sql语句,后边的参数为占位符对应的参数
		jdbcTemplate.update(sql, "jerry",1000);
	}
	//删除数据
	public void delete() {
		String sql = "delete from account where name=?";
		jdbcTemplate.update(sql, "jerry");
	}
	//更新数据
	public void update() {
		String sql = "update account set money=500 where name=?";
		jdbcTemplate.update(sql,"jerry");
	}
	//查询所有
	public List findAll(){
		String sql ="select * from account";
		//与dbutils类似,在dbutils使用new beanListHandler<>().这里使用new BeanPropertyRowMapper<>()
		List list = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Account.class));
		return list;
	}
	//查询单个对象
	public Account findOne(){
		String sql ="select * from account where money=?";
		//查询单个对象还是用new BeanPropertyRowMapper<>(),只不过方法不是用query,而是queryForObject
		Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Account.class),500);
		return account;
	}
	//聚合查询
	public Integer getCount() {
		String sql ="select count(*) from account";
		//查询总记录数,使用queryForObject,第二个参数为返回的类型class对象
		Long count = jdbcTemplate.queryForObject(sql, Long.class);
		return count.intValue();
	}
}

  • Service层示例
package com.wzm.service;

import java.util.List;

import com.wzm.dao.AccountDao;
import com.wzm.entity.Account;

/*
 * service层
 */
public class AccountService {
	//定义AccountDao对象和set方法,使用依赖注入
	private AccountDao accountDao;
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	//保存
	public void save() {
		accountDao.save();
	}
	//删除
	public void delete() {
		accountDao.delete();
	}
	//更新
	public void update() {
		accountDao.update();
	}
	//查询所有
	public List findAll() {
		return accountDao.findAll();
	}
	//查询单个
	public Account findOne() {
		return accountDao.findOne();
	}
	//聚合查询
	public Integer getCount() {
		return accountDao.getCount();
	}
}

  • 测试示例
package com.wzm.test;

import java.util.List;

import javax.annotation.Resource;

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

import com.wzm.entity.Account;
import com.wzm.service.AccountService;
import com.wzm.service.AccountServiceImpl;

/*
 * 使用整合junit的方式做测试
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:applicationContext.xml")
public class TxTest {
	
	//注解方式注入AccountService
	@Resource(name="accountService")
	private AccountService accountService;

	@Resource(name="accountServiceImpl")
	private AccountServiceImpl accountServiceImpl;
	
	@Test
	public void test1() {
		accountService.save();
	}
	
	@Test
	public void test2() {
		accountService.delete();
	}
	
	@Test
	public void test3() {
		accountService.update();
	}
	
	@Test
	public void test4() {
		List list = accountService.findAll();
		for (Account account : list) {
			System.out.println(account);
		}
	}
	
	@Test
	public void test5() {
		Account account = accountService.findOne();
		System.out.println(account);
	}
	
	@Test 
	public void test6() {
		Integer count = accountService.getCount();
		System.out.println(count);
	}
}

#声明式事务的使用-xml方式

##Spring的事务控制API

spring的声明式事务原理上都是基本编码式的事务,因此需要先了解下spring关于事务控制的API。

  • Spring的事务控制API
  1. PlatformTransactionManager

Spring的事务管理器,这是一个接口,接口中提供了事务操作的基本方法
commit()——事务提交
rollBack()——事务回滚
PlatformTransactionManager的具体实现类
DataSourceTransactionManager
如果是使用Spring的Jdbc方式就使用该类
HibernateTransactionManager
如果使用的是整合了Hibernate的jdbc就使用这个类

  1. TransactionDefinition

获取事务参数的一个类
获取事务的隔离级别
Read_uncommitted——读未提交
Read_commited——读已提交 Oracle默认级别,解决脏读问题
Repeatable_read——可重复读 MySQL默认级别,解决脏读+不可重复读问题
Serializable——可序列读 最高级别,解决脏读+不可重复读+幻读问题
获取事务的传播方式
假设a方法调用b方法,b方法有事务
REQUIRED—如果a方法有事务,就直接使用a的事务,如果没事务,就为a创建事务
SUPPORTS—如果a方法有事务,就使用a的事务,如果没事务,就使用无事务方式
MANDATORY—a方法有事务就直接用,没有就抛出异常
REQUERS_NEW—a方法没有事务就创建事务,如果a有事务也不使用,直接再创建一个新的事务。
获取事务是否只读
只读—只有在查询时才会开启事务
非只读—所有操作都开启事务
获取事务的名称
获取事务的超时时间

  1. TransactionStatus

这是一个获取事务状态的接口
获取事务是否是新事务
获取事务是否回滚
获取事务是否完成

在spring中要使用声明式事务需要有aop的支持,因此要需要导入aop相关的jar包,同样还需要导入:
spring-jdbc-4.2.4.RELEASE.jar
spring-tx-4.2.4.RELEASE.jar

  • 配置文件示例



        
        
        
        
        
        
        	
        	
        	
        	
        
        
        
        
        	
         
        
        
        
        	
        
        
        
        
        	
        
        
        
        
        	
        
        
		
        
        	
        		
        		
        	
        
        
        
        
        	
        	
        	
        	
        

  • Dap层示例
package com.wzm.dao;

import org.springframework.jdbc.core.JdbcTemplate;

/*
 * Dao层
 */
public class AccountDaoImpl {
	
	//使用set方式依赖注入JdbcTemplate对象
	private JdbcTemplate jdbcTemplate;
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	//收款方加钱操作
	public void addMoney(String name, Double money) {
		String sql = "update account set money=money+? where name=?";
		jdbcTemplate.update(sql,money,name);
	}

	//转账方减钱操作
	public void reduceMoney(String name, Double money) {
		String sql = "update account set money=money-? where name=?";
		jdbcTemplate.update(sql,money,name);
	}
}

  • Service层示例
package com.wzm.service;

import com.wzm.dao.AccountDaoImpl;

/*
 * service层
 */
public class AccountServiceImpl {
	//使用set方式依赖注入AccountDaoImpl对象
	private AccountDaoImpl accountDaoImpl;
	public void setAccountDaoImpl(AccountDaoImpl accountDaoImpl) {
		this.accountDaoImpl = accountDaoImpl;
	}
	/*	事务的切入点
	 * fromname	转账方
	 * toname	收款方
	 * money	转账金额
	 */
	public void transfer(String fromname,String toname,Double money) {
		//转账方减钱
		accountDaoImpl.reduceMoney(fromname, money);
		//制造异常
		int i = 1/0;
		//收款方加钱
		accountDaoImpl.addMoney(toname, money);
	}
}

  • 测试
package com.wzm.test;

import java.util.List;

import javax.annotation.Resource;

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

import com.wzm.entity.Account;
import com.wzm.service.AccountService;
import com.wzm.service.AccountServiceImpl;

/*
 * 使用整合junit的方式做测试
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:applicationContext.xml")
public class TxTest {
	
	//注解方式注入AccountService
	@Resource(name="accountService")
	private AccountService accountService;

	@Resource(name="accountServiceImpl")
	private AccountServiceImpl accountServiceImpl;
	
	@Test
	public void test7() {
		accountServiceImpl.transfer("jerry", "tom", 500.0);
	}
}

  • 实体类示例
package com.wzm.entity;

/*
 * Account类
 */
public class Account {
	String name;
	Double money;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getMoney() {
		return money;
	}
	public void setMoney(Double money) {
		this.money = money;
	}
	@Override
	public String toString() {
		return "Account [name=" + name + ", money=" + money + "]";
	}
	
}

#声明式事务的使用-注解方式

在xml方式中,配置文件的配置比较复杂,但是如果使用注解,一切就会变得非常简便。

  • 配置文件示例



        
        
        
        
         
        
        
        
        	
        	
        	
        	
        
        
        
        
        	
         
        
         
        
        	
        
        
        
        
        
        
        
        

  • Dap层示例
package com.wzm.dao;

import javax.annotation.Resource;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

/*
 * Dao层 --注解方式
 */
@Repository("accountDaoImpl")
public class AccountDaoImpl {
	
	//使用set方式依赖注入JdbcTemplate对象
	@Resource(name="jdbcTemplate")
	private JdbcTemplate jdbcTemplate;

	//收款方加钱操作
	public void addMoney(String name, Double money) {
		String sql = "update account set money=money+? where name=?";
		jdbcTemplate.update(sql,money,name);
	}

	//转账方减钱操作
	public void reduceMoney(String name, Double money) {
		String sql = "update account set money=money-? where name=?";
		jdbcTemplate.update(sql,money,name);
	}
}

  • Service层示例
package com.wzm.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.wzm.dao.AccountDaoImpl;

/*
 * service层——注解方式
 */
@Service("accountServiceImpl")
public class AccountServiceImpl {
	//使用set方式依赖注入AccountDaoImpl对象
	@Resource(name="accountDaoImpl")
	private AccountDaoImpl accountDaoImpl;
	
	/*	事务的切入点
	 * fromname	转账方
	 * toname	收款方
	 * money	转账金额
	 * @Transactional指明在这个方法上开启事务
	 * 	也可以定义在类上,对所有方法都开启事务
	 */
	
	@Transactional(propagation=Propagation.REQUIRED,timeout=-1,readOnly=false)
	public void transfer(String fromname,String toname,Double money) {
		//转账方减钱
		accountDaoImpl.reduceMoney(fromname, money);
		//制造异常
		int i = 1/0;
		//收款方加钱
		accountDaoImpl.addMoney(toname, money);
	}
}

你可能感兴趣的:(Spring框架学习笔记)