美文网首页
java基础-day53-Spring03

java基础-day53-Spring03

作者: 触手不可及 | 来源:发表于2021-07-12 08:39 被阅读0次

    今日内容

    spring
    

    Spring中的事务

    事务:是逻辑上一组操作,要么全都成功,要么全都失败.
    事务特性:ACID

    原子性:事务不可分割
    一致性:事务执行的前后,数据完整性保持一致.
    隔离性:一个事务执行的时候,不应该受到其他事务的打扰
    持久性:一旦结束,数据就永久的保存到数据库.

    如果不考虑隔离性:
    脏读:一个事务读到另一个事务未提交数据
    不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致
    虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致

    事务的隔离级别:

    未提交读:以上情况都有可能发生。
    已提交读:避免脏读,但不可重复读,虚读是有可能发生。
    可重复读:避免脏读,不可重复读,但是虚读有可能发生。
    串行的:避免以上所有情况.

    Spring中事务管理
    分层开发:事务处在Service层.
    

    Spring提供事务管理API

    
    
    PlatformTransactionManager:平台事务管理器.
        getTransaction(TransactionDefinition definition) 
        rollback(TransactionStatus status) 
        commit(TransactionStatus status) 
        
    TransactionDefinition:事务定义
        ISOLation_XXX:事务隔离级别.
        PROPAGATION_XXX:事务的传播行为.
        
    TransactionStatus:事务状态
        是否有保存点
        是否是一个新的事务
        事务是否已经提交
        
    关系:PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态,状态由TransactionStatus记录。
    
    API详解:
        PlatformTransactionManager:接口.
        Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现
    
    使用Spring JDBC或iBatis 进行持久化数据时使用(重点)
        org.springframework.jdbc.datasource.DataSourceTransactionManager
    使用Hibernate进行持久化数据时使用
        org.springframework.orm.hibernate.HibernateTransactionManager
    使用JPA进行持久化时使用   
        org.springframework.orm.jpa.JpaTransactionManager   
    当持久化机制是Jdo时使用   
        org.springframework.jdo.JdoTransactionManager
    使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用    
        org.springframework.transaction.jta.JtaTransactionManager   
        
    TransactionDefinition:
        ISOLATION_DEFAULT:默认级别. Mysql --> repeatable_read | Oracle -->> read_commited
        
        ISOLATION_READ_UNCOMMITTED
        ISOLATION_READ_COMMITTED 
        ISOLATION_REPEATABLE_READ 
        ISOLATION_SERIALIZABLE 
    
    事务的传播行为:(不是JDBC事务管理,用来解决实际开发的问题.)
    传播行为:解决业务层之间的调用的事务的关系.
    PROPAGATION_REQUIRED:   支持当前事务,如果不存在 就新建一个
    * A,B   如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)
    PROPAGATION_SUPPORTS:   支持当前事务,如果不存在,就不使用事务
    * A,B   如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.
    PROPAGATION_MANDATORY:  支持当前事务,如果不存在,抛出异常
    * A,B   如果A有事务,B使用A的事务,如果A没有事务,抛出异常.
    PROPAGATION_REQUIRES_NEW:   如果有事务存在,挂起当前事务,创建一个新的事务
    * A,B   如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)
    PROPAGATION_NOT_SUPPORTED:  以非事务方式运行,如果有事务存在,挂起当前事务
    * A,B   非事务的方式运行,A有事务,就会挂起当前的事务.
    PROPAGATION_NEVER:  以非事务方式运行,如果有事务存在,抛出异常
    PROPAGATION_NESTED: 如果当前事务存在,则嵌套事务执行
    * 基于SavePoint技术.
    * A,B   A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚.
    
    * 常用:(重点)
    PROPAGATION_REQUIRED     
    PROPAGATION_REQUIRES_NEW
    PROPAGATION_NESTED
    

    Spring的事务管理分成两类

    编程式事务管理:
    手动编写代码完成事务管理.
    声明式事务管理:
    不需要手动编写代码,配置.

    事务操作的环境搭建
    
    1.创建表
        CREATE TABLE `account` (
          `id` int(11) NOT NULL AUTO_INCREMENT,
          `name` varchar(20) NOT NULL,
          `money` double DEFAULT NULL,
          PRIMARY KEY (`id`)
        ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
        INSERT INTO `account` VALUES ('1', 'aaa', '1000');
        INSERT INTO `account` VALUES ('2', 'bbb', '1000');
    
    2.创建项目,导入依赖
        <dependencies>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</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.aspectj</groupId>
                    <artifactId>aspectjweaver</artifactId>
                    <version>1.8.13</version>
                </dependency>
    
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-jdbc</artifactId>
                    <version>5.0.2.RELEASE</version>
                </dependency>
    
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>druid</artifactId>
                    <version>1.0.9</version>
                </dependency>
    
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.32</version>
                </dependency>
    
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>4.12</version>
                </dependency>
            </dependencies>
    
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <target>1.8</target>
                            <source>1.8</source>
                            <encoding>UTF-8</encoding>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
    
    3.创建接口和类
    
        1.AccountDao:
        
        public interface AccountDao {
    
            // 加钱
            void increaseMoney(Integer id, Double money);
    
            // 减钱
            void decreaseMoney(Integer id, Double money);
        }
        
        2.AccountDaoImpl:
    
        public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao  {
    
            @Override
            public void increaseMoney(Integer id, Double money) {
    
                getJdbcTemplate().update("update account set money = money+? where id = ? ", money,id);
    
            }
    
            @Override
            public void decreaseMoney(Integer id, Double money) {
    
                getJdbcTemplate().update("update account set money = money-? where id = ? ", money,id);
            }
    
        }
        
        3.AccountService:
    
        public interface AccountService {
    
            //转账方法
            void transfer(Integer from,Integer to,Double money);
    
        }
    
        4.AccountServiceImpl:
    
        public class AccountServiceImpl implements AccountService {
    
            private AccountDao accountDao;
    
            @Override
            public void transfer(Integer from, Integer to, Double money) {
                // 减钱
                accountDao.decreaseMoney(from, money);
                // int i = 1 / 0;// 如果发生异常数据(钱)会丢失
                // 加钱
                accountDao.increaseMoney(to, money);
            }
    
            public void setAccountDao(AccountDao accountDao) {
                this.accountDao = accountDao;
            }
        }
    
    4.创建applicationContext.xml引入事务(tx)约束
    
        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-4.2.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">
    
    
            <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="url" value="jdbc:mysql://localhost:3306/mydb"></property>
                <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
                <property name="username" value="root"></property>
                <property name="password" value="root"></property>
            </bean>
    
            <bean name="accountDao" class="com.AccountDaoImpl">
                <property name="dataSource" ref="dataSource"></property>
            </bean>
    
            <bean name="accountService" class="com.AccountServiceImpl">
                <property name="accountDao" ref="accountDao"></property>
            </bean>
    
        </beans>
    
    5.测试
        public class Demo {
    
            @Test
            public void test(){
                ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
                AccountService accountService = (AccountService)context.getBean("accountService");
    
                accountService.transfer(1,2,100d);
    
            }
        }
    

    上述测试中,如果在转账方法中出现异常后,数据前后会产生不一致,此时,我们需要用Spring的事务管理来解决这一问题。

    手动编码的方式完成事务管理:(了解)
    缺点:代码量增加,代码有侵入性.
    
    修改AccountServiceImpl
    
    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
    
        private TransactionTemplate transactionTemplate;
    
        @Override
        public void transfer(final Integer from,final Integer to,final Double money) {
    
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus arg0) {
                    // 减钱
                    accountDao.decreaseMoney(from, money);
                    int i = 1 / 0;// 如果发生异常数据(钱)不会丢失
                    // 加钱
                    accountDao.increaseMoney(to, money);
    
                }
            });
        }
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
            this.transactionTemplate = transactionTemplate;
        }
    }
    
    修改applicationContext.xml
    
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="url" value="jdbc:mysql://localhost:3306/mydb"></property>
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="username" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
    
        <!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
        <bean name="transactionManager"
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!-- 事务模板对象 -->
        <bean name="transactionTemplate"
              class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager" ref="transactionManager"></property>
        </bean>
    
        <bean name="accountDao" class="com.AccountDaoImpl">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <bean name="accountService" class="com.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
            <property name="transactionTemplate" ref="transactionTemplate"></property>
        </bean>
    
    Xml配置(aop)的方式完成事务管理
    
    修改AccountServiceImpl
    
    public class AccountServiceImpl implements AccountService {
    
            private AccountDao accountDao;
    
            @Override
            public void transfer(Integer from, Integer to, Double money) {
                // 减钱
                accountDao.decreaseMoney(from, money);
                // int i = 1 / 0;// 如果发生异常数据(钱)会丢失
                // 加钱
                accountDao.increaseMoney(to, money);
            }
    
            public void setAccountDao(AccountDao accountDao) {
                this.accountDao = accountDao;
            }
        }
    

    企业中配置CRUD方法一般使用方法名+通配符*的形式配置通知,此时类中的方法名要和配置的方法名一致
    以方法为单位,指定方法应用什么事务属性
    isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
    propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
    read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
    timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
    rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
    no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。

    修改applicationContext.xml
    
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="url" value="jdbc:mysql://localhost:3306/mydb"></property>
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="username" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
    
        <!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
        <bean name="transactionManager"
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 配置事务通知 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
              
                <tx:method name="save*" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="false" />
                <tx:method name="persist*" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="false" />
                <tx:method name="update*" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="false" />
                <tx:method name="modify*" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="false" />
                <tx:method name="delete*" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="false" />
                <tx:method name="remove*" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="false" />
                <tx:method name="get*" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="true" />
                <tx:method name="find*" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="true" />
                <tx:method name="transfer" isolation="REPEATABLE_READ"
                           propagation="REQUIRED" read-only="false" />
            </tx:attributes>
        </tx:advice>
    
        <!-- 配置织入 -->
        <aop:config>
            <!-- 配置切点表达式 -->
            <aop:pointcut expression="execution(* com.qf.service.*ServiceImpl.*(..))" id="txPc" />
            <!-- 配置切面 : 通知+切点 advice-ref:通知的名称 pointcut-ref:切点的名称 -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" />
        </aop:config>
    
        <bean name="accountDao" class="com.AccountDaoImpl">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <bean name="accountService" class="com.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
        </bean>
    
    注解配置(aop)的方式完成事务管理
    
    修改applicationContext.xml
    
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/mydb"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    
    <!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
    <bean name="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 开启spring对注解事务的支持-->
    <tx:annotation-driven />
    
    <bean name="accountDao" class="com.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <bean name="accountService" class="com.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    
    修改AccountServiceImpl
    
    @Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
    
        @Override
        //如果该方法与类名上的配置不同,可以单独在这个方法上配置注解
        @Transactional(isolation= Isolation.REPEATABLE_READ,propagation= Propagation.REQUIRED,readOnly=false)
        public void transfer(Integer from, Integer to, Double money) {
            // 减钱
            accountDao.decreaseMoney(from, money);
            int i = 1 / 0;// 如果发生异常数据(钱)会丢失
            // 加钱
            accountDao.increaseMoney(to, money);
        }
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    }
    

    相关文章

      网友评论

          本文标题:java基础-day53-Spring03

          本文链接:https://www.haomeiwen.com/subject/qocrpltx.html