Spring框架的事务管理

Spring框架的事务管理

事务:指的是逻辑上一组操作,组成这个事务的各个执行单元,要么一起成功,要么一起失败。

事务的特性:

  1. 原子性:(Atomicity)组成这个事务的各个执单元,不可再分割
  2. 一致性:(Consistency)事务执行后数据库状态与其他规则保持一致,如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的。
  3. 隔离性:(Isolation)隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰
  4. 持久性:(Durability)一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务之后,数据库马上崩溃,在数据库重启时,也必须保证通过某种机制恢复数据。

如果不考虑隔离性,引发安全性问题

读问题:

脏读:一个事务会读取到另一个事务未提交的数据,造成数据错误,是不允许的

不可重复读:A事务中读取到了B事务中已经提交的数据,在特定情境下会产生影响,比如生成统一的数据报表。

虚度:A事务中读取到了B事务中已经提交的新插入的数据。

写问题

丢失更新

如何解决安全性问题:

读问题解决:设置数据隔离级别

写问题解决可以采用悲观锁和乐观所方式来解决。

  1. 完成一个转账的功能,需要进行事务的管理,使用Spring的事务管理的方式完成
步骤一:创建数据库的表结构
	create database spring_day03;
	use spring_day03;
	create table t_account(
		id int primary key auto_increment,
		name varchar(20),
		money double
	);
步骤二:引入开发所需要的jar包
<dependency>
		            <groupId>org.springframework</groupId>
		            <artifactId>spring-context</artifactId>		            <version>5.0.2.RELEASE</version>
		        </dependency>
		        <dependency>
		            <groupId>org.aspectj</groupId>
		            <artifactId>aspectjweaver</artifactId>
		            <version>1.8.7</version>
		        </dependency>
        	<dependency>
		            <groupId>junit</groupId>		             						<artifactId>junit</artifactId>
		            <version>4.12</version>
		        </dependency>
		        <dependency>
		            <groupId>org.springframework</groupId>
		            <artifactId>spring-test</artifactId>
		            <version>5.0.2.RELEASE</version>
		        </dependency>
		        <dependency>
		            <groupId>org.springframework</groupId>
		            <artifactId>spring-jdbc</artifactId>
		            <version>5.0.2.RELEASE</version>		        				</dependency>
		
	       <dependency>
	            <groupId>org.springframework</groupId>
	            <artifactId>spring-tx</artifactId>
	            <version>5.0.2.RELEASE</version>
	        </dependency>
	
	        <dependency>
	            <groupId>mysql</groupId>
	            <artifactId>mysql-connector-java</artifactId>
	            <version>5.1.6</version>
	        </dependency>

使用Spring框架来管理模板类

自己new对象的方式

@Test
		public void run1(){
			// 创建连接池,先使用Spring框架内置的连接池
			DriverManagerDataSource dataSource = new DriverManagerDataSource();
			dataSource.setDriverClassName("com.mysql.jdbc.Driver");
			dataSource.setUrl("jdbc:mysql:///spring_day03");
			dataSource.setUsername("root");
			dataSource.setPassword("123456");
			// 创建模板类
			JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);  //参数要一个连接池,可以使用Spring内置的链接池,可以使用第三方的 例如 C3P0,DBCP
			// 完成数据的添加
			jdbcTemplate.update("insert into t_account values (null,?,?)", "测试",10000);
		}

Spring框架来管理模板类

步骤一:Spring管理内置的连接池
		<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	    	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
	    	<property name="url" value="jdbc:mysql:///spring_day03"/>
	    	<property name="username" value="root"/>
	    	<property name="password" value="123456"/>
	    </bean>
步骤二:Spring管理模板类
		<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	    	<property name="dataSource" ref="dataSource"/>
	    </bean>
编写测试程序
步骤三:编写测试程序
		@RunWith(SpringJUnit4ClassRunner.class)
		@ContextConfiguration("classpath:applicationContext.xml")
		public class Demo2 {
			
			@Resource(name="jdbcTemplate")
			private JdbcTemplate jdbcTemplate;
			
			@Test
			public void run2(){
				jdbcTemplate.update("insert into t_account values (null,?,?)", "测试2",10000);
			}
		}

管理DBCP连接池

是Maven项目添加DBCP的jar包坐标
      <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>com.springsource.org.apache.commons.pool</artifactId>
            <version>1.5.3</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.2.2</version>
        </dependency>
 编写配置文件
		<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
	    	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
	    	<property name="url" value="jdbc:mysql:///spring_day03"/>
	    	<property name="username" value="root"/>
	    	<property name="password" value="root"/>
	    </bean>
管理C3P0连接池
添加C3P0的jar包
<dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.2.1</version>
        </dependency>
  编写配置文件
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	    	<property name="driverClass" value="com.mysql.jdbc.Driver"/>
	    	<property name="jdbcUrl" value="jdbc:mysql:///spring_day03"/>
	    	<property name="user" value="root"/>
	    	<property name="password" value="root"/>
	    </bean>

Spring框架的JDBC模板的简单操作

实现用户的增删改查操作

测试:

@RunWith(SpringJUnit4ClassRunner.class)
	@ContextConfiguration("classpath:applicationContext.xml")
	public class SpringDemo3 {
		
		@Resource(name="jdbcTemplate")
		private JdbcTemplate jdbcTemplate;
		
		@Test
		// 插入操作
		public void demo1(){
			jdbcTemplate.update("insert into t_account values (null,?,?)", "cherry",10000d);
		}
		
		@Test
		// 修改操作
		public void demo2(){
			jdbcTemplate.update("update t_account set name=?,money =? where id = ?", "lisa",10000d,5);
		}
		
		@Test
		// 删除操作
		public void demo3(){
			jdbcTemplate.update("delete from t_account where id = ?", 5);
		}
		

@Test
		// queryForObject()方法 查询一条记录 也就是返回一行记录  写一个JavaBean 然后查询一条数据,帮你封装进这个JavaBean
		public void demo4(){
			Account account = jdbcTemplate.queryForObject("select * from account where id = ?", new BeanMapper(), 1);
			System.out.println(account);
		}
		
		@Test
		// 查询所有记录 使用query方法 直接返回一个集合 这个集合装有所有记录
		public void demo5(){
			List<Account> list = jdbcTemplate.query("select * from t_account", new BeanMapper());
			for (Account account : list) {
				System.out.println(account);
			}
		}
	}
	//定义一个类 实现RowMapper接口 自己重写方法,把查出的数据封装到 JavaBean中
	class BeanMapper implements RowMapper<Account>{
		public Account mapRow(ResultSet rs, int arg1) throws SQLException {
			//这里不用循环,因为他是一行一行来封装的 
			Account account = new Account();
			account.setId(rs.getInt("id"));
			account.setName(rs.getString("name"));
			account.setMoney(rs.getDouble("money"));
			return account;
		}
	}
查询一条或多条记录的另外一种封装方式 BeanPropertyRowMapper
 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
        //我们查询出来的数据直接封装到JavaBean里面,然后再放到集合中
        //那么我们可以使用BeanPropertyRowMapper 来做
        List<Account> list = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), 4);
        System.out.println(list);
继承Spring提供的JdbcDaoSupport类
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
	//在继承了JdbcDaoSupport后,我们就可以不需要再dao里面注入jdbcTemplate而是从父类获取一个用
	/* private JdbcTemplate jdbcTemplate;
    @Override
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }*/
	
    public List<Account> findAll() {
        List<Account> query = super.getJdbcTemplate().query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
        return query;
    }
}
上面的dao继承了 JdbcDaoSupport 后,我们可以通过getJdbcTemplate()来获取一个JdbcTemplate对象
那么配置文件中就可以直接注入数据源
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///spring_test"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!--配置JDBC-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean class="org.westos.dao.AccountDaoImpl" id="accountDao">
    	注入数据源,那么父类JdbcDaoSupport就会创建JDBCTemplate
        <property name="dataSource" ref="dataSource"></property>
        
        在继承了JdbcDaoSupport我们就不需要注入jdbcTemplate了
        <!--<property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
    </bean>
Spring框架的事务管理相关的类和API
  1. PlatformTransactionManager接口:平台事务管理器(真正故管理事务的类),这个接口有具体的实现类,根据不同的持久成框架,选择不同的实体类。

  2. TransactionDefinition接口:事务定义信息(事务的隔离级别,传播行为,超时,只读)

  3. TransactionStatus接口:事务的状态。

  4. 平台事务管理器真正管理事务对象.根据事务定义的信息TransactionDefinition 进行事务管理,在管理事务中产生一些状态.将状态记录到TransactionStatus中

  5. PlatformTransactionManager接口中实现类和常用的方法。

    1. 接口的实现类

      如果使用的Spring的JDBC模板或者MyBatis框架,需要选择DataSourceTransactionManager实现类

      如果使用的是Hibernate的框架,需要选择HibernateTransactionManager实现类

    2. 该接口的常用方法

      void commit(TransactionStatus status)

      TransactionStatus getTransaction(TransactionDefinition definition)

      void rollback(TransactionStatus status)

  6. TransactionDefinition 事务定义信息

    1. 事务隔离级别的常量。

      static int ISOLATION_DEFAULT – 采用数据库的默认隔离级别

      static int ISOLATION_READ_UNCOMMITTED
      static int ISOLATION_READ_COMMITTED
      static int ISOLATION_REPEATABLE_READ
      static int ISOLATION_SERIALIZABLE

    2. 事务的传播行为常量,不用设置,使用默认值

      事务的传播行为:解决的是业务层之间的方法调用!

      PROPAGATION_REQUIRED(默认值) – A中有事务,使用A中的事务.如果没有,B就会开启一个新的事务,将A包含进来.(保证A,B在同一个事务中),默认值!!

      PROPAGATION_SUPPORTS – A中有事务,使用A中的事务.如果A中没有事务.那么B也不使用事务.

      PROPAGATION_MANDATORY – A中有事务,使用A中的事务.如果A没有事务.抛出异常.

      PROPAGATION_REQUIRES_NEW(记)-- A中有事务,将A中的事务挂起.B创建一个新的事务.(保证A,B没有在一个事务中)

      PROPAGATION_NOT_SUPPORTED – A中有事务,将A中的事务挂起.

      PROPAGATION_NEVER – A中有事务,抛出异常.

      PROPAGATION_NESTED – 嵌套事务.当A执行之后,就会在这个位置设置一个保存点.如果B没有问题.执行通过.如果B出现异常,运行客户根据需求回滚(选择回滚到保存点或者是最初始状态)

搭建事务管理账户案例的环境
步骤一:引入jar包
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
步骤二:引入配置文件
引入log4j.properties   日志的配置文件
引入applicationContext.xml   注意约束要写全了
	<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">
			 <!--配置C3P0的连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql:///spring_day03"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!--配置jdbc的模板-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--引入Spring的配置文件,将类配置到Spring中-->
    <bean id="accountDao" class="com.itheima.demo1.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean id="accountService" class="com.itheima.demo1.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"/>
    </bean>

        </beans>	
public interface AccountDao {
    void outMoney(String out, double money);
    void inMoney(String in, double money);
}
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    public void outMoney(String out, double money) {
        this.getJdbcTemplate().update("update t_account set money = money - ? where name = ?", money,out);
    }

    public void inMoney(String in, double money) {
        this.getJdbcTemplate().update("update t_account set money = money + ? where name = ?",money,in);
    }
}
public interface AccountService {

    void pay(String name1, String name2, int i);
}
public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void pay(String name1, String name2, int i) {
    //记得一定要调用这俩方法
        accountDao.outMoney(name1,i);
        accountDao.inMoney(name2,i);
        System.out.println("ok");
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MyTest {
    @Resource(name = "accountService")
    private AccountService accountService;
    @Test
    public void run1(){
        accountService.pay("lisa", "cherry", 1000);
    }
}

Spring框架的事务管理之基于AspectJ的XML方式

  1. 配置事务管理器

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="dataSource"/>
    	</bean>
    
  2. 配置事务增强 注意先配置事务的增强再配置切面

    配置事务增强,关联事务管理器
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
    		<tx:method name="pay" propagation="REQUIRED"/>
    		</tx:attributes>
    	</tx:advice>
    name:绑定事务的方法名,可以使用通配符,可以配置多个
    isolation:用于指定事务的隔离级别,默认值是DAFAULT,表示使用数据库的默认隔离级别。
    propagation:用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事务,增删改的选择,查询方法可以选择SUPPORTS
    read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
     timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
      rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
      no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
      哪些方法加事务 tx:method 可以出现多个,你可以配置多个方法 方法名也可以通配 比如 pay* 
    
  3. 配置AOP得出切面

    配置AOP切面产生代理
    <aop:config>
        		<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.demo2.AccountServiceImpl.pay(..))"/>
    	</aop:config>
    	如果是自己编写的切面,使用<aop:aspect>标签来配置我们写的切面类,如果是Spring框架提供的切面,配置切面类使用<aop:advisor>标签。
    
  4. 编写测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    	@ContextConfiguration("classpath:applicationContext2.xml")
    	public class Demo2 {
    		
    		@Resource(name="accountService")
    		private AccountService accountService;
    		
    		@Test
    		public void run1(){
    			accountService.pay("cherry", "lisa", 1000);
    		}
    	}
    

Spring框架的事务管理之基于AspectJ的注解方式

  1. 配置事务管理器

    配置事务管理器
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="dataSource"/>
    	</bean>
    
  2. 在配置文件中开启注解事务,把平台事务管理器传进去

    <!-- 开启注解事务 -->
    	<tx:annotation-driven transaction-manager="transactionManager"/>
    
  3. 在业务层上添加一个注解:@Transactional

    @Transactional 这个注解 加在类上,那么这个类中所有的方法都有事务了
    如果加在某个方法上,那这个方法就有事务了,一般我们加在类上

    @Transactional 加在方法上这个注解里面里 有一些属性就可以设置,比如 隔离级别,事务的传播行为等等

  4. 编写测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    	@ContextConfiguration("classpath:applicationContext3.xml")
    	public class Demo3 {
    		@Resource(name="accountService")
    		private AccountService accountService;
    		@Test
    		public void run1(){
    			accountService.pay("cherry", "lisa", 1000);
    		}
    	}
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值