Spring4-声明式事务管理

使用spring连接mysql:

<!--  组件扫描  -->
<context:component-scan base-package="com.kgc.spring"/>

<!--  spring读取外部properties配置文件  -->
<!--  普通写法:从当前类路径下读取配置文件,classpath:只有spring才能识别 -->
<!--<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties"/>
</bean>-->
<!--  高级写法:从当前类路径下读取配置文件,classpath:只有spring才能识别 -->
<!--  此种写法,自动将读取的配置跟系统环境变量放在一起,可能导致读取的用户名为电脑的系统用户,而不是root
        建议:配置数据库连接信息时,增加一个前缀,比如:mysql.
  -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!--  配置数据源  -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${mysql.driverClass}"/>
    <property name="jdbcUrl" value="${mysql.jdbcUrl}"/>
    <property name="user" value="${mysql.user}"/>
    <property name="password" value="${mysql.password}"/>
</bean>

<!--  spring的持久层框架-JdbcTemplate  -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!-- 指定数据源 -->
    <property name="dataSource" ref="dataSource"/>
</bean>
  1. 组件扫描:扫描是不可能省的,这辈子都不能省的
  2. 读取外部properties配置文件:高级写法祝您轻松码代码
  3. 配置数据源:繁琐的配置环节
  4. JdbcTemplate配置:spring的数据库框架,用了都说好!(其实没人用)
在需要用到的地方使用注解导入即可使用:
@Autowired
private JdbcTemplate jdbcTemplate;

事务管理:

  • 原子性:

    原子即不可再分,表现:一个事务涉及的多个操作在业务逻辑上缺一不可,保证同一个事务中的操作要不都提交,要不都不提交

  • 一致性:

    数据的一致性,一个事务中,不管涉及到多少个操作,都必须保证数据提交的正确性(一致),即:如果在事务数据处理中,有一个或者几个操作失败,必须回退所有的数据操作,恢复到事务处理之前的统一状态

  • 隔离性:

    程序运行过程中,事务是并发执行的,要求每个事务之间都是隔离的,互不干扰

  • 持久性:

    事务处理结束,要讲数据进行持久操作,即永久保存。

编程式事务管理

使用原生的 JDBC API 实现事务管理是所有事务管理方式的基石,同时也是最典型的编程式事务管理。编程式事务管理需要将事务管理代码嵌入到业务方法中来控制事务 的提交和回滚。在使用编程的方式管理事务时,必须在每个事务操作中包含额外的事务 管理代码。相对于核心业务而言,事务管理的代码显然属于非核心业务,如果多个模块 都使用同样模式的代码进行事务管理,显然会造成较大程度的代码冗余

声明式事务管理

事务管理代码的固定模式作为一种横切关注点,可以通过 AOP 方法模块化,进而借助 Spring AOP 框架实现声明式事务管理。

事务使用说明:

  1. spring配置
  2. 在需要使用事务的地方使用注解
spring中配置事务管理器和事务扫描:
<!--  配置事务管理器  -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--  开启事务注解扫描,transaction-manager属性默认值是transactionManager,自定义事务管理器,必须通过该属性指定  -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
注解的使用:@Transactional
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED, noRollbackFor = {UserMoneyException.class})
public void buyCar(String buyerName, Integer carId) {
    // 根据购买汽车编号,获取汽车的价格
    Double carPrice = buyCarDao.selectCarPriceById(carId);

    // 扣减汽车库存
    buyCarDao.subCarStock(carId);

    // 扣减账户余额
    buyCarDao.subUserLeftMoney(buyerName, carPrice);

    System.out.println("------ 买家: " + buyerName + ",购买汽车编号:" + carId + ",成功 ------");
}

@Transactional注解的属性说明:

propagation:事务的传播行为

​ 当一个带事务的方法被另一个带事务的方法调用时,当前事务如何处理(这就是事务的传播行为):

  • propagation = Propagation.REQUIRED : 默认值,使用调用者的事务(全程就一个事务)
  • propagation = Propagation.REQUIRES_NEW : 将调用者的事务直接挂起,自己重开新的事务处理,结束提交事务,失败回滚
isolation:事务的隔离级别

​ 指定隔离级别,只有InnoDB支持事务,所有这里说的事务隔离级别指的是InnoDB下的事务隔离级别。

  • 读未提交 读取其它事务未提交的数据,了解,基本不会使用

  • 读已提交 oracle的默认事务隔离级别,同一个事务处理中,只能读取其它事务提交后的数据(也就是说事务提交之前对其余事务不可见)

  • 可重复读 mysql默认事务隔离级别,同一个事务处理中,多次读取同一数据是都是一样的,不受其它事务影响

  • 串行化 可以避免上面所有并发问题,但是执行效率最低,数据一致性最高

noRollbackFor:事务的指定是否回滚

​ Spring在默认的情况下,是对所有的运行时异常会执行事务回滚。

  • rollbackFor : 指定回滚异常,只有产生了指定的异常类型,才会回滚事务

  • noRollbackFor : 指定不会滚异常,产生了指定的异常类型,也不会回滚事务

timeout:事务的超时时长

​ timeout,指定事务出现异常,没有及时回滚,单位是秒,防止事务超时,占用资源

readOnly事务的超时和只读属性
  • readOnly=false,默认,可读可写
  • readOnly=true,代表该事务处理,理论上只允许读取,不能修改(只是通知spring,并不是一个强制选项)
  • 目的就是:提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
  • 但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。

Q.E.D.