• spring系统学习--5事务控制


    最原始的事务控制

    //        注册驱动
            DriverManager.registerDriver(new com.mysql.jdbc.Driver());
    //        Class.forName("com.mysql.jdbc.Driver");
    //        获取连接
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring5?useSSL=false", "root", "admin123");
    //        Connection对象可以设置事务提交方式:分为自动提交和手动提交;默认是自动提交
            connection.setAutoCommit(false);
    //        获取预处理对象
            PreparedStatement preparedStatement = connection.prepareStatement("insert into account (name,money)values (?,?)");
            preparedStatement.setString(1,"ccc");
            preparedStatement.setFloat(2,1555f);
            int i = preparedStatement.executeUpdate();
            connection.commit();
            System.out.println(i);
            preparedStatement.close();
            connection.close();

    Spring事务模板使用

    spring配置文件内容:

    <!--    配置事务管理器-->
        <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="comboPooledDataSource"></property>
        </bean>
    <!--    配置spring事务模板-->
        <bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
            <property name="transactionManager" ref="TransactionManager"></property>
        </bean>
     <bean id="accountServiceImpl" class="service.impl.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
            <property name="transactionTemplate" ref="transactionTemplate"></property>
        </bean>
    <!--    创建数据源的bean:使用ComboPooledDataSource数据源需要导入外部jar包:c3p0-->
        <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring5"></property>
            <property name="user" value="root"></property>
            <property name="password" value="admin123"></property>
        </bean>

    业务逻辑层实现类:

    //账户业务层实现类
    public class AccountServiceImpl implements AccountService {
        private AccountDao accountDao;
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        //    声明事务控制模板
        private TransactionTemplate transactionTemplate;
    
        public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
            this.transactionTemplate = transactionTemplate;
        }
    
        @Override
        public void transfer(String sourseName, String targetName, Float money) {
            transactionTemplate.execute(new TransactionCallback<Object>() {
                @Override
                public Object doInTransaction(TransactionStatus transactionStatus) {
                    //        根据账户名查询对应的账户
                    Account sourceAccount = accountDao.findAccountByName(sourseName);
                    Account targetAccount = accountDao.findAccountByName(targetName);
    //        转出账户减钱,转入账户加钱
                    sourceAccount.setMoney(sourceAccount.getMoney() - money);
                    targetAccount.setMoney(targetAccount.getMoney() + money);
    //        更新两个账户
                    accountDao.updateAccount(sourceAccount);
    //        模拟异常
                    int i = 1 / 0;
                    accountDao.updateAccount(targetAccount);
                    return null;
                }
            });
    
        }
    
        @Override
        public Account findAccountById(Integer id) {
            return accountDao.findAccountById(id);
        }
    }

    测试类代码:

    public class Client {
        public static void main(String[] args) {
    //      获取容器
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            AccountServiceImpl accountServiceImpl = (AccountServiceImpl) context.getBean("accountServiceImpl");
            accountServiceImpl.transfer("aaa","bbb",100f);
            context.close();
        }

    Spring基于XML的声明式事务控制(配置方式)重点(开发中首选)

    需要事务控制的代码:

        
    //账户业务层实现类
    public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    
    public void setAccountDao(AccountDao accountDao) {
         this.accountDao = accountDao;
    }
    @Override
        public void transfer(String sourseName, String targetName, Float money) {
    //            根据账户名查询对应的账户
                    Account sourceAccount = accountDao.findAccountByName(sourseName);
                    Account targetAccount = accountDao.findAccountByName(targetName);
    //            转出账户减钱,转入账户加钱
                    sourceAccount.setMoney(sourceAccount.getMoney() - money);
                    targetAccount.setMoney(targetAccount.getMoney() + money);
    //           更新两个账户
                    accountDao.updateAccount(sourceAccount);
    //            模拟异常
                    int i = 1 / 0;
                    accountDao.updateAccount(targetAccount);
        }
        @Override
        public Account findAccountById(Integer id) {
            return accountDao.findAccountById(id);
        }
    }

    Spring配置文件:

    <?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">
        <!--    将jdbc模板交给spring管理-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="comboPooledDataSource"></property>
        </bean>
        <!--    创建数据源的bean:使用ComboPooledDataSource数据源需要导入外部jar包:c3p0-->
        <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring5"></property>
            <property name="user" value="root"></property>
            <property name="password" value="admin123"></property>
        </bean>
        <bean id="accountServiceImpl" class="service.impl.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
            <property name="transactionTemplate" ref="transactionTemplate"></property>
        </bean>
        <bean id="accountDao" class="dao.impl.AccountDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
            <property name="dataSource" ref="comboPooledDataSource"></property>
        </bean>
        <!--    配置事务管理器-->
        <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="comboPooledDataSource"></property>
        </bean>
        <!--    使用tx名称空间下面的标签配置事务的通知-->
        <tx:advice id="txAdvice" transaction-manager="TransactionManager">
            <!--        配置事务属性
            isolation=用于指定事务的隔离级别,默认值是"DEFAULT",表示使用数据库的隔离级别
            timeout=用于指定事务的过期时间,默认值"-1",永不过期,如果设置了该值,以秒为单位
            read-only=用于指定事务是否只读,默认值"false",不只读.只有查询方法才可以设置为true
            propagation=用于指定事务的传播行为,默认值是"REQUIRED",表示一定有事务,一般增删改设置为REQUIRED;查询设置为SUPPORT,表示,你有事务我就要有,你没有,我也没有;
            rollback-for=用于指定用一个异常,当产生该异常时,事务回滚。产生其他异常时,事务不回滚。没有默认值,表示任何异常都回滚。
            no-rollback-for=用于指定一个异常,当产生该异常时,事务不回滚。产生其他异常时,事务回滚。没有默认值,表示任何异常都回滚。
            -->
            <tx:attributes>
                <tx:method name="*" read-only="false"/><!--*表示对将来使用通知的所有方法(增删改)使用此配置-->
                <tx:method name="find*" read-only="true"/><!--加入将来我们所有的查询方法都以fing开头,那么find*表示对查询方法使用此配置-->
            </tx:attributes>
        </tx:advice>
        <!--    配置AOP-->
        <aop:config>
            <!--        配置切入点表达式-->
            <aop:pointcut id="pt" expression="execution(* service..*.*(..))"/>
            <!--建立切入点表达式和事务通知之间的关系-->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
        </aop:config>
    </beans>

    事务属性补充图片:

    测试代码:

    public class Client {
        public static void main(String[] args) {
    //      获取容器
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            AccountService accountService = (AccountService) context.getBean("accountServiceImpl");
            accountService.transfer("aaa","bbb",100f);
            context.close();
        }
    }

    Spring基XML和注解组合事务控制(配置方式)重点

    spring配置文件代码:

    <?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">
        <!--    配置Spring扫描注解的包-->
        <context:component-scan base-package="service"></context:component-scan>
        <context:component-scan base-package="dao"></context:component-scan>
        <!--    将jdbc模板交给spring管理-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="comboPooledDataSource"></property>
        </bean>
        <!--    创建数据源的bean:使用ComboPooledDataSource数据源需要导入外部jar包:c3p0-->
        <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring5"></property>
            <property name="user" value="root"></property>
            <property name="password" value="admin123"></property>
        </bean>
        <!--    配置事务管理器-->
        <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="comboPooledDataSource"></property>
        </bean>
    <!--    开启Spring对注解事务的支持,并引用事务管理器-->
        <tx:annotation-driven transaction-manager="TransactionManager"></tx:annotation-driven>
    </beans>

    需要事务控制的类需要添加@Transactional注解:

    //账户业务层实现类
    @Service("accountService")
    /**
     * @Transactional可以出现在接口上,类上和方法
     * 接口上:表示该接口中的所有方法,实现类中重写的方法都有事务
     * 类上:表示该类中所有的方法都有事务
     * 方法上:表示当前方法有事务
     * 优先级:就近原则。
     * 方法>类>接口
     */
    @Transactional(readOnly = false)
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        @Override
        public void transfer(String sourseName, String targetName, Float money) {
            //        根据账户名查询对应的账户
            Account sourceAccount = accountDao.findAccountByName(sourseName);
            Account targetAccount = accountDao.findAccountByName(targetName);
    //        转出账户减钱,转入账户加钱
            sourceAccount.setMoney(sourceAccount.getMoney() - money);
            targetAccount.setMoney(targetAccount.getMoney() + money);
    //        更新两个账户
            accountDao.updateAccount(sourceAccount);
    //        模拟异常
            int i = 1 / 0;
            accountDao.updateAccount(targetAccount);
    
        }
    
        @Override
        public Account findAccountById(Integer id) {
            return accountDao.findAccountById(id);
        }
    }

    测试类代码:

    public class Client {
        public static void main(String[] args) {
    //      获取容器
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            AccountService accountService = (AccountService) context.getBean("accountService");
            accountService.transfer("aaa","bbb",100f);
            Account account = accountService.findAccountById(1);
            System.out.println(account);
            context.close();
        }
    }

     基于注解的声明式事务控制(配置方式)重点

    创建一个类用于加载spring的配置并指定要扫描的包

    /**
     * 用于初始化spring容器的配置类
     */
    @Configuration
    @ComponentScan(basePackages="com.itheima")
    public class SpringConfiguration {
    
    }

    创建业务层接口和实现类并使用注解让spring管理

    /**
     * 账户的业务层实现类
     */
    @Service("accountService")
    public class AccountServiceImpl implements IAccountService {
        @Autowired
        private IAccountDao accountDao;
    
        @Override
        public Account findAccountById(Integer id) {
            return accountDao.findAccountById(id);
        }
    
        @Override
        public void transfer(String sourceName, String targeName, Float money) {
            //1.根据名称查询两个账户
            Account source = accountDao.findAccountByName(sourceName);
            Account target = accountDao.findAccountByName(targeName);
            //2.修改两个账户的金额
            source.setMoney(source.getMoney()-money);//转出账户减钱
            target.setMoney(target.getMoney()+money);//转入账户加钱
            //3.更新两个账户
            accountDao.updateAccount(source);
            int i=1/0;
            accountDao.updateAccount(target);
        }
    }

    创建Dao接口和实现类并使用注解让spring管理

    Dao层接口和AccountRowMapper与基于xml配置的时候相同。略
    
    @Repository("accountDao")
    public class AccountDaoImpl implements IAccountDao {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
        
        @Override
        public Account findAccountById(Integer id) {
            List<Account> list = jdbcTemplate.query("select * from account where id = ? ",new AccountRowMapper(),id);
            return list.isEmpty()?null:list.get(0);
        }
    
        @Override
        public Account findAccountByName(String name) {
            List<Account> list =  jdbcTemplate.query("select * from account where name = ? ",new AccountRowMapper(),name);
            if(list.isEmpty()){
                return null;
            }
            if(list.size()>1){
                throw new RuntimeException("结果集不唯一,不是只有一个账户对象");
            }
            return list.get(0);
        }
    
        @Override
        public void updateAccount(Account account) {
            jdbcTemplate.update("update account set money = ? where id = ? ",account.getMoney(),account.getId());
        }
    }

    使用@Bean注解配置数据源

    @Bean(name = "dataSource")
    public DataSource createDS() throws Exception {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setUsername("root");
    dataSource.setPassword("123");
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://spring3_day04");
    return dataSource;
    }

    使用@Bean注解配置配置事务管理器

    @Bean
    public PlatformTransactionManager
    createTransactionManager(@Qualifier("dataSource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
    }

    使用@Bean注解配置JdbcTemplate

    @Bean
    public JdbcTemplate createTemplate(@Qualifier("dataSource") DataSource dataSource)
    {
    return new JdbcTemplate(dataSource);
    }

    在需要控制事务的业务层实现类上使用@Transactional注解

    @Service("accountService")
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public class AccountServiceImpl implements IAccountService {
        @Autowired
        private IAccountDao accountDao;
        @Override
        public Account findAccountById(Integer id) {
            return accountDao.findAccountById(id);
        }
        @Override
        @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
        public void transfer(String sourceName, String targeName, Float money) {
    //1.根据名称查询两个账户
            Account source = accountDao.findAccountByName(sourceName);
            Account target = accountDao.findAccountByName(targeName);
    //2.修改两个账户的金额
            source.setMoney(source.getMoney() - money);//转出账户减钱
            target.setMoney(target.getMoney() + money);//转入账户加钱
    //3.更新两个账户
            accountDao.updateAccount(source);
    //int i=1/0;
            accountDao.updateAccount(target);
        }
    }

    该注解的属性和xml中的属性含义一致。该注解可以出现在接口上,类上和方法上。

    出现接口上,表示该接口的所有实现类都有事务支持。

    出现在类上,表示类中所有方法有事务支持

    出现在方法上,表示方法有事务支持。

    以上三个位置的优先级:方法>>接口。

    使用@EnableTransactionManagement开启spring对注解事务的的支持

    @Configuration
    @EnableTransactionManagement
    public class SpringTxConfiguration {
    //里面配置数据源,配置JdbcTemplate,配置事务管理器。在之前的步骤已经写过了。
    }
  • 相关阅读:
    JSON解析之——Android
    Xml解析之——Java/Android/Python
    Design Pattern —— Singleton
    设计模式(10)--观察者模式
    设计模式(9)--建造者模式
    设计模式(8)--外观模式
    设计模式(7)--模板模式
    设计模式(6)--原型模式
    设计模式(5)--工厂模式
    设计模式(4)--代理模式
  • 原文地址:https://www.cnblogs.com/luzhanshi/p/13258595.html
Copyright © 2020-2023  润新知