zoukankan      html  css  js  c++  java
  • 1.1(Spring学习笔记)Spring-事务基础

    一、Spring 事务

      Spring提供对事务支持的核心包是spring-tx-4.3.6.RELEASE包。

      该包类有三个核心接口,提供对事务的支持:

      1.1PlatformTransactionManager

      该接口是Spring提供的平台管理器,主要用于事务管理。

      该接口提供了如下方法:

      TranscationStatus getTransaction(TransactionDefinition definition);用于获取事务状态信息。

      void commit(TransactionStatus status):提交事务

      void rollback(TransactionStatus status):事务回滚

      此接口的具体实现类:

      org.springframework.jdbc.datasource.DataSourceTransactionManager(用于配置JDBC)。

      1.2TransactionDefinition

      TransactionDefinition是事务定义的对象,包含定义事务规则,获取事务信息等操作。

      String getName();获取事务对象名称。

      int getLsolationLeve();获取事务隔离级别。

      int getPropagationBehavior();获取事务传播行为。

      int getTimeout();获取事务超时时间。

      boolean isReadOnly()判断事务是否只读。

      

      1.3TransactionStatus

      TransactionStatus是事务的状态,包含方法:

      void flush();刷新事务

      boolean isCompleted();判断事务是否完成。

      boolean isNewTranscation();判断是否为新事务。

      boolean isRollbackOnly();判断事务是否回滚。

      void setRollbackOnly:设置事务回滚。

    二、声明式事务管理

      声明式事务管理式通过AOP将事务管理作为一个切面,然后将切面(事务管理)织入目标类中。

      先看一个失败案例,未配置事务,进行数据库操作失败情况:

      Account.java

    public class Account {
        private Integer id;
        private String username;
        private Double balance;
        
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public Double getBalance() {
            return balance;
        }
        public void setBalance(Double balance) {
            this.balance = balance;
        }
        
        @Override
        public String toString() {
            return "Account [id=" + id + ", username=" + username + ", balance=" + balance + "]";
        }
        
    }

    AccountDao.java(定义数据库相关操作)

    import java.util.List;
    
    public interface AccountDao {
        public int addAccount(Account account);
        public int updateAccount(Account account);
        public int deleteAccount(int id);
        public Account findAccountById(int id);
        public List<Account> findAllAccounts();
        public void transfer(String outUser, String inUser, Double money);//转账
    }

    AccountDaoImpl.java (AccountDao的实现类)

    import java.util.List;
    
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.RowMapper;
    
    public class AccountDaoImpl implements AccountDao{
        private JdbcTemplate jt;
        
        public void setJt(JdbcTemplate jt) {
            this.jt = jt;
        }
    
        @Override //添加用户
        public int addAccount(Account account) {
            String sql = "insert into account(username,balance) value(?,?)";
            Object[] obj = new Object[] {
                account.getUsername(),
                account.getBalance()
            };
            int num = jt.update(sql,obj);
            return num;
        }
    
        @Override   //更新用户
        public int updateAccount(Account account) {
            String sql = "update account set username=?,balance=?,where id = ?";
            Object[] obj = new Object[] {
                    account.getUsername(),
                    account.getBalance(),
                    account.getId()
            };
            int num = jt.update(sql,obj);
            return num;
        }
    
        @Override  //删除用户
        public int deleteAccount(int id) {
            String sql = "delete from account where id = ?";
            int num = jt.update(sql,id);
            return num;
        }
    
        @Override  //按ID查找
        public Account findAccountById(int id) {
            String sql = "select * from account where id = ?";
            //设置映射,即表中一行记录对应一个Accout对象(数据库中列名要和对象属性名一致)
            RowMapper<Account> rp = new BeanPropertyRowMapper<Account>(Account.class);
            return this.jt.queryForObject(sql, rp, id);
        }
    
        @Override//查找所有对象
        public List<Account> findAllAccounts() {
            String sql = "select * from account";
            RowMapper<Account> rp = new BeanPropertyRowMapper<Account>(Account.class);
            return this.jt.query(sql,rp);
        }
    
        @Override //转账
        public void transfer(String outUser, String inUser, Double money) {
            //转账
            this.jt.update("update account set balance = balance - ?" +
                            "where username = ?", money, outUser);
            int i = 1/0;  //模拟意外情况。
            //收账
            this.jt.update("update account set balance = balance + ?" +
                            "where username = ?",money,inUser);
        }
    }

     beans.xml

    <?xml  version="1.0"  encoding="UTF-8"?> 
    <beans  xmlns="http://www.springframework.org/schema/beans" 
            xmlns:aop="http://www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xmlns:context="http://www.springframework.org/schema/context"         
            xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://www.springframework.org/schema/beans 
                http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                http://www.springframework.org/schema/context 
                 http://www.springframework.org/schema/context/spring-context-4.3.xsd
                 http://www.springframework.org/schema/tx
                 http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
                 http://www.springframework.org/schema/aop
                 http://www.springframework.org/schema/aop/spring-aop-4.3.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/spring" />
            <property name = "username" value = "root" />
            <property name = "password" value = "123456" />
        </bean>
        
        <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
            <property name = "dataSource" ref = "dataSource" />
        </bean>
        
        <bean id = "accountDao" class = "com.spring.db.AccountDaoImpl">
            <property name="jt" ref = "jdbcTemplate"></property>
        </bean>
    
    </beans>

    测试:

      

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    public class JDBCTemplateTest {
        public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
            AccountDao accountDao = (AccountDao)ac.getBean("accountDao");
            Account hcf = new Account();
            Account zrx = new Account();
            //设置两个用户
            hcf.setUsername("hcf");
            hcf.setBalance(1000.0);
        
            zrx.setUsername("zrx");
            zrx.setBalance(1000.0);
            //将用户添加到数据库
            accountDao.addAccount(hcf);
            accountDao.addAccount(zrx);
            //zrx向hcf转账500
            accountDao.transfer("zrx", "hcf", 500.0);
            System.out.println("成功");
        }
    }

     转账方法中人为添加一个错误,诸侯出现了转账的金额扣除了,但是收账方没有收到金额。

    这种情况显然是我们不允许的。

    这时就可以通过事务来实现,把转账功能作为一个事务。

    即使中途出现错误没有将钱转给收账方,会发生回滚转账方的钱也不会扣除。

      

      2.1基于XML的声明式事务

      首先我们需要如下jar包:

      

      先了解下xml中配置事务的元素:

      <tx:advice>配置事务通知,

      属性transaction-manager:指定事务管理器。

        <tx:attributes>:<tx:advice>的子元素,用于配置事务细节。

          <tx:method>:<tx:attributes>的子元素,用于配置会务相关信息。

          属性:name:指定与事务关联的方法,可采用通配符*。

             propagation:指定事务传播行为。

             isolation;指定事务隔离级别,默认为REFAULT。

             read-only:指定事务是否只读。

      

      我们只需在benas.xml中配置事务相关信息,然后通过切面的配置,将目标方法织入即可。

    <?xml  version="1.0"  encoding="UTF-8"?> 
    <beans  xmlns="http://www.springframework.org/schema/beans" 
            xmlns:aop="http://www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xmlns:context="http://www.springframework.org/schema/context"         
            xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://www.springframework.org/schema/beans 
                http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                http://www.springframework.org/schema/context 
                 http://www.springframework.org/schema/context/spring-context-4.3.xsd
                 http://www.springframework.org/schema/tx
                 http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
                 http://www.springframework.org/schema/aop
                 http://www.springframework.org/schema/aop/spring-aop-4.3.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/spring" />
            <property name = "username" value = "root" />
            <property name = "password" value = "123456" />
        </bean>
        
        <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
            <property name = "dataSource" ref = "dataSource" />
        </bean>
        
        <bean id = "accountDao" class = "com.spring.db.AccountDaoImpl">
            <property name="jt" ref = "jdbcTemplate"></property>
        </bean>
        
        <!-- 事务管理 -->
        <!-- 配置事务管理,并制定数据源 -->
        <bean id = "transactionManager" 
                class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name = "dataSource" ref = "dataSource" />
        </bean>
        <!-- 配置事务切面,将事务作为一个切面织入方法 -->
        <tx:advice id = "txAdvice" transaction-manager = "transactionManager">
            <tx:attributes>
                <!-- *代表任意方法           传播行为                                     隔离级别                             是否只读 -->
                <tx:method name = "*" propagation = "REQUIRED" isolation = "DEFAULT" read-only ="false"/>
            </tx:attributes>
        </tx:advice>
        
        <!-- 配置切面,制定切面,切入点 -->
        <aop:config>
            <aop:pointcut expression = "execution(* com.spring.db.*.*(..))" id = "txPointCut"/>
            <aop:advisor advice-ref = "txAdvice" pointcut-ref = "txPointCut"/>
        </aop:config>
    
    </beans>

    在xml中添加事务配置,我们再来进行转账操作。

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    public class JDBCTemplateTest {
        public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
            AccountDao accountDao = (AccountDao)ac.getBean("accountDao");
            Account hcf = new Account();
            Account zrx = new Account();
            
            //zrx向hcf转账500
            accountDao.transfer("zrx", "hcf", 500.0);
            System.out.println("成功");
        }
    }

    再次运行出现错误后数据库中的balance并没有被扣除。

    转账和收账作为一个整体,一荣俱荣,一损俱损。

      2.2基于Annotation的声明式事务

        1)在xml中配置事务注解驱动

        <tx:annotation-driven transaction-manager="transactionManager"/>

        2)在需要使用事务的类(对类中所有方法有效)或方法(只对方法有效)上添加@Transactional注解。

        

       @Transactional注解属性:

       transactionManager:指定事务管理器。

       isolation:指定事务隔离级。

       noRollbackFor:用于指定遇到异常时强制不执行回滚。

       noRollbackForClassName:指定遇到多个异常时强制不执行回滚。

       该属性可指定多个异常类名。

       propagation:指定事务传播行为。

       read-only:设置事务是否只读,默认为false。

       rollbackFor:用于指定遇到特定异常时强制发生回滚。

       rollbackForClassName:指定遇到多个异常时强制执行回滚。

       timeout:指定事务超时时长。

      beans.xml

      

    <?xml  version="1.0"  encoding="UTF-8"?> 
    <beans  xmlns="http://www.springframework.org/schema/beans" 
            xmlns:aop="http://www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xmlns:context="http://www.springframework.org/schema/context"         
            xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://www.springframework.org/schema/beans 
                http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                http://www.springframework.org/schema/context 
                 http://www.springframework.org/schema/context/spring-context-4.3.xsd
                 http://www.springframework.org/schema/tx
                 http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
                 http://www.springframework.org/schema/aop
                 http://www.springframework.org/schema/aop/spring-aop-4.3.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/spring" />
            <property name = "username" value = "root" />
            <property name = "password" value = "123456" />
        </bean>
        
        <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
            <property name = "dataSource" ref = "dataSource" />
        </bean>
        
        <bean id = "accountDao" class = "com.spring.db.AccountDaoImpl">
            <property name="jt" ref = "jdbcTemplate"></property>
        </bean>
        
        <!-- 事务管理 -->
        
        <!-- 配置事务管理,并制定数据源 -->
        <bean id = "transactionManager" 
                class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name = "dataSource" ref = "dataSource" />
        </bean>
        <!-- 配置事务驱动 -->
        <tx:annotation-driven transaction-manager = "transactionManager" />
    
    </beans>

    将转账方法加上注解:

    @Override //转账
        @Transactional(propagation = Propagation.REQUIRED,
                       isolation = Isolation.DEFAULT, readOnly = false)
        public void transfer(String outUser, String inUser, Double money) {
            //转账
            this.jt.update("update account set balance = balance - ?" +
                            "where username = ?", money, outUser);
            int i = 1/0;  //模拟意外情况。
            //收账
            this.jt.update("update account set balance = balance + ?" +
                            "where username = ?",money,inUser);
        }

       

  • 相关阅读:
    => 运算符
    ASP.NET与Javascript中获取URL信息
    ASP.NET中存取图片到数据库的示例
    在C#中利用DirectX实现声音播放
    使用VS2005 DataSet设计器实现数据访问层
    用C#播放声音文件
    VB.NET中声音的播放
    用C#播放声音文件
    声音播放解决方案(C#)
    Asp.Net音频文件上传和播放
  • 原文地址:https://www.cnblogs.com/huang-changfan/p/10446932.html
Copyright © 2011-2022 走看看