04.spring

6 篇文章 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;
        }
    }
修改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>
            <!-- 企业中配置CRUD方法一般使用方法名+通配符*的形式配置通知,此时类中的方法名要和配置的方法名一致 -->
            <!-- 以方法为单位,指定方法应用什么事务属性
                isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
                propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
                read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
                timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
                rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
                no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
             -->
            <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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值