11- Spring声明式事务管理

14.1spring事务管理

14.1.1 事务定义

#事务(transaction),一般是指要做的或者所做的事情。
	在程序中,尤其是在操作数据库的程序中,指的是访问并且可能更新数据库中数据项的一个执行单元(unit),
	这个执行单元由事务开始(begin transaction)和事务结束(end transaction)之间执行的全部操作组成

14.1.2 事务ACID原则

#事务具有4个基本特性:原子性、一致性、隔离性、持久性。也就是我们常说的ACID原则:

    #1.原子性(Atomicity):
    一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做

    #2.一致性(Consistency):
    事务必须是使得数据库状态从一个一致性状态,转变到另外一个一致性状态。
    也就是说在事务前,和事务后,被操作的目标资源状态一致。
    比如银行转账案例中,转账前和转账后,总账不变。

    #3.隔离性(Isolation):
    一个事务的执行不能被其他事务的影响。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,
    多个并发事务之间不能相互干扰

    #4.持久性(Durability):
    一个事务一旦提交,它对数据库中数据的改变会永久存储起来其他操作不会对它产生影响。

14.2 spring声明式事务管理

14.2.1 项目目录(xml配置)

14.2.2 编写实体类

package cn.guardwhy.domain;

/**
 * 账户实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    
    
    // 成员变量
    private int id;
    private String name;
    private Float money;
}

14.2.3 结果集映射类

package cn.guardwhy.rowmapper;

import cn.guardwhy.domain.Account;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 账户结果集映射类
 */
public class AccountRowMapper implements RowMapper<Account>{
    
    

    /**
     *每一行记录,调用一次该方法
     */
    public Account mapRow(ResultSet rs, int index) throws SQLException {
    
    

        // 创建Account
        Account account = new Account();

        account.setId(rs.getInt("id"));
        account.setName(rs.getString("name"));
        account.setMoney(rs.getFloat("money"));

        return account;
    }
}

14.2.4 持久层(Dao)

  • dao接口
package cn.guardwhy.dao;

import cn.guardwhy.domain.Account;

/**
 * 账户dao接口
 */
public interface AccountDao {
    
    

    /**
     * 根据账户id查询账户
     */
    Account findAccountById(Integer accountId);

    /**
     * 根据账户名称查询账户
     */
    Account findAccountByName(String accountName);

    /**
     * 更新账户
     */
    void updateAccount(Account account);
}
  • dao实现类
package cn.guardwhy.dao.impl;

import cn.guardwhy.dao.AccountDao;
import cn.guardwhy.domain.Account;
import cn.guardwhy.rowmapper.AccountRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

/**
 * 账户dao实现类
 */
public class AccountDaoImpl  implements AccountDao{
    
    

    // 定义JdbcTemplate
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    
    
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 根据账户id查询账户
     *
     * @param accountId
     */
    public Account findAccountById(Integer accountId) {
    
    
        List<Account> list = jdbcTemplate.query("select * from account where id=?", 
        new AccountRowMapper(), accountId);

        return list.isEmpty()?null:list.get(0);
    }

    /**
     * 根据账户名称查询账户
     *
     * @param accountName
     */
    public Account findAccountByName(String accountName) {
    
    
        List<Account> list = jdbcTemplate.query("select * from account where name=?", 
        new AccountRowMapper(), accountName);

        // 判断不为空
        if(list.isEmpty()){
    
    
            return null;
        }

        // 判断账户是否唯一
        if(list.size()>1){
    
    
            throw new RuntimeException("账户不唯一,请检查!");
        }

        return list.get(0);
    }

    /**
     * 更新账户
     *
     * @param account
     */
    public void updateAccount(Account account) {
    
    
        jdbcTemplate.update("update account set money=? 
        where id=?",account.getMoney(),account.getId());
    }
}

14.2.5 业务层(Service)

  • service接口
package cn.guardwhy.service;

import cn.guardwhy.domain.Account;

/**
 * 账户service接口
 */
public interface AccountService {
    
    

    /**
     * 根据账户id查询账户
     */
    Account findAccountById(Integer accountId);

    /**
     * 转账操作:
     *  参数说明:
     *      sourceName:转出账户
     *      destName:转入账户
     *      money:转账金额
     *
     */
    void transfer(String sourceName,String destName,Float money);
}
  • service实现类
package cn.guardwhy.service.impl;

import cn.guardwhy.dao.AccountDao;
import cn.guardwhy.domain.Account;
import cn.guardwhy.service.AccountService;

/**
 * 账户service实现类
 */
public class AccountServiceImpl implements AccountService{
    
    

    // 定义账户dao
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
    
    
        this.accountDao = accountDao;
    }

    /**
     * 根据账户id查询账户
     *
     * @param accountId
     */
    public Account findAccountById(Integer accountId) {
    
    
        return accountDao.findAccountById(accountId);
    }

    /**
     * 转账操作:
     * 参数说明:
     * sourceName:转出账户
     * destName:转入账户
     * money:转账金额
     *
     * @param sourceName
     * @param destName
     * @param money
     */
    public void transfer(String sourceName, String destName, Float money) {
    
    

        // 1.查询转出账户
        Account sourceAccount = accountDao.findAccountByName(sourceName);

        // 2.查询转入账户
        Account destAccount = accountDao.findAccountByName(destName);

        // 3.转出账户-money
        sourceAccount.setMoney(sourceAccount.getMoney()-money);

        // 4.转入账户+money
        destAccount.setMoney(destAccount.getMoney()+money);

        // 5.更新转出账户
        accountDao.updateAccount(sourceAccount);
        
        // 6.更新转入账户
        accountDao.updateAccount(destAccount);

    }
}

14.3 声明式事务配置

14.3.1配置事务管理器

<!--第一步:配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源对象-->
    <property name="dataSource" ref="dataSource"></property>
</bean>

14.3.2 配置aop和切入点表达式

<!--第二步:配置aop与切入点表达式-->
<aop:config>
    <aop:pointcut id="pt1" expression="execution(* cn.guardwhy.service..*.*(..))"> </aop:pointcut>
</aop:config>

14.3.3 建立通知与切入点表达式关系

<!--第二步:配置aop与切入点表达式-->
<aop:config>
    <aop:pointcut id="pt1" expression="execution(* cn.guardwhy.service..*.*(..))"></aop:pointcut>
    
    <!--第三步:建立事务管理器(通知)与切入点表达式关系-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>

14.3.4 配置通知

<!--第四步:配置通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    
</tx:advice>

14.3.5 配置事务属性

<!--第四步:配置通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--第五步:配置事务属性-->
    <tx:attributes>
        <!--tx:method标签:配置业务方法的名称规则,说明:
            name:方法名称规则,可以使用通配符*
            isolation:事务隔离级别,使用默认值即可
            propagation:事务传播行为,增/删/改方法使用REQUIRED。查询方法使用SUPPORTS。
            read-only:是否只读事务。增/删/改方法使用false。查询方法使用true。
            timeout:配置事务超时。使用默认值-1即可。永不超时。
            rollback-for:发生某种异常时,回滚;发生其它异常不回滚。没有默认值,任何异常都回滚
            no-rollback-for:发生某种异常时不回滚;发生其它异常时回滚。没有默认值,任何异常都回滚。
        -->
        <tx:method name="transfer*"  propagation="REQUIRED" read-only="false"/>
    </tx:attributes>
</tx:advice>

14.3.6 配置bean.xml

<?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: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/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">

    <!--配置账户service-->
    <bean id="accountService" class="cn.guardwhy.service.impl.AccountServiceImpl">
        <!--注入账户dao-->
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!--配置账户dao-->
    <bean id="accountDao" class="cn.guardwhy.dao.impl.AccountDaoImpl">
        <!--注入JdbcTemplate-->
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入数据源对象-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置数据源对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--注入连接数据库的四个基本要素-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>

        <!--注入连接池公共属性-->
        <!-- 初始化连接数量 -->
        <property name="initialSize" value="6" />
        <!-- 最小空闲连接数 -->
        <property name="minIdle" value="3" />
        <!-- 最大并发连接数(最大连接池数量) -->
        <property name="maxActive" value="50" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
    </bean>

    <!--配置声明式事务-->
    <!--第一步:配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源对象-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--第二步:配置aop和切入点表达式-->
    <aop:config>
        <aop:pointcut id="pt1" expression="execution(* cn.guardwhy.service..*.*(..))"></aop:pointcut>

        <!--第三步:建立通知和切入点表达式的关系-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

    <!--第四步:配置通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
         <!--第五步:配置事务属性-->
        <tx:attributes>
            <tx:method name="transfer*" propagation="REQUIRED"   read-only="false" />
        </tx:attributes>
    </tx:advice>
    
</beans>

14.3.7 表现层(Controller)

  • Controller
package cn.guardwhy.controller;

import cn.guardwhy.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 账户表现层
 */
public class AccountController {
    
    

    public static void main(String[] args) {
    
    
        // 1.加载spring配置文件,创建spring ioc容器
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");

        // 2.获取账户service
        AccountService accountService = (AccountService)context.getBean("accountService");

        // 3.转账操作
        accountService.transfer("curry","james",200f);
    }
}

14.4 xml和注解声明式事务配置

14.4.1项目目录

14.4.2 持久层(Dao)

  • 客户dao实现类
package cn.guardwhy.dao.impl;

import cn.guardwhy.dao.AccountDao;
import cn.guardwhy.domain.Account;
import cn.guardwhy.rowmapper.AccountRowMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 账户dao实现类
 */
@Repository("accountDao")
public class AccountDaoImpl  implements AccountDao{
    
    

    // 定义JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    
    
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 根据账户id查询账户
     *
     * @param accountId
     */
    public Account findAccountById(Integer accountId) {
    
    
        List<Account> list = jdbcTemplate.query("select * from account where id=?", new AccountRowMapper(), accountId);

        return list.isEmpty()?null:list.get(0);
    }

    /**
     * 根据账户名称查询账户
     *
     * @param accountName
     */
    public Account findAccountByName(String accountName) {
    
    
        List<Account> list = jdbcTemplate.query("select * from account where name=?", new AccountRowMapper(), accountName);

        // 判断不为空
        if(list.isEmpty()){
    
    
            return null;
        }

        // 判断账户是否唯一
        if(list.size()>1){
    
    
            throw new RuntimeException("账户不唯一,请检查!");
        }

        return list.get(0);
    }

    /**
     * 更新账户
     *
     * @param account
     */
    public void updateAccount(Account account) {
    
    
        jdbcTemplate.update("update account set money=? where id=?",account.getMoney(),account.getId());
    }
}

14.4.3 业务层(Service)

  • 客户service实现类
package cn.guardwhy.service.impl;

import cn.guardwhy.dao.AccountDao;
import cn.guardwhy.domain.Account;
import cn.guardwhy.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 账户service实现类
 */
@Service("accountService")
public class AccountServiceImpl implements AccountService{
    
    

    // 定义账户dao
    @Autowired
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
    
    
        this.accountDao = accountDao;
    }

    /**
     * 根据账户id查询账户
     *
     * @param accountId
     */
    public Account findAccountById(Integer accountId) {
    
    
        return accountDao.findAccountById(accountId);
    }

    /**
     * 转账操作:
     * 参数说明:
     * sourceName:转出账户
     * destName:转入账户
     * money:转账金额
     * @param sourceName
     * @param destName
     * @param money
     */
    public void transfer(String sourceName, String destName, Float money) {
    
    
        // 1.查询转出账户
        Account sourceAccount = accountDao.findAccountByName(sourceName);

        // 2.查询转入账户
        Account destAccount = accountDao.findAccountByName(destName);

        // 3.转出账户-money
        sourceAccount.setMoney(sourceAccount.getMoney()-money);

        // 4.转入账户+money
        destAccount.setMoney(destAccount.getMoney()+money);

        // 5.更新转出账户
        accountDao.updateAccount(sourceAccount);

        // 6.更新转入账户
        accountDao.updateAccount(destAccount);

    }
}

14.4.4 配置bean.xml

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置包扫描dao/service-->
    <context:component-scan base-package="cn.guardwhy"/>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入数据源对象-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置数据源对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--注入连接数据库的四个基本要素-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>

        <!--数据库连接池常用属性-->
        <!-- 初始化连接数量 -->
        <property name="initialSize" value="6" />
        <!-- 最小空闲连接数 -->
        <property name="minIdle" value="3" />
        <!-- 最大并发连接数(最大连接池数量) -->
        <property name="maxActive" value="50" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
    </bean>

    <!--spring事务管理配置步骤-->
    <!--第一步:配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源对象-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--第二步:开启spring对注解事务的支持 -->
    <tx:annotation-driven/>
</beans>

14.4.5 表现层(Controller)

  • Controller
package cn.guardwhy.controller;

import cn.guardwhy.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 账户表现层
 */
public class AccountController {
    
    

    public static void main(String[] args) {
    
    
        // 1.加载spring配置文件,创建spring ioc容器
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");

        // 2.获取账户service
        AccountService accountService = (AccountService)context.getBean("accountService");

        // 3.转账操作
        accountService.transfer("curry","james",200f);
    }
}

14.4.6 执行结果

14.5 注解声明式事务配置

14.5.1 项目目录

14.5.2 持久层配置类

package cn.guardwhy.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/**
 * 持久层配置类
 */
public class DaoConfiguration {
    
    
    /**
     * 创建JdbcTemplate对象
     */
    @Bean("jdbcTemplate")
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
    
    
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    /**
     * 创建数据源对象
     */
    @Bean("dataSource")
    public DataSource createDataSource(){
    
    
        // 创建DataSource
        DruidDataSource dataSource = new DruidDataSource();

        // 注入属性
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        dataSource.setInitialSize(6);
        dataSource.setMinIdle(3);
        dataSource.setMaxActive(50);
        dataSource.setMaxWait(60000);
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        return dataSource;
    }
}

14.5.2 事务配置类

package cn.guardwhy.config;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * 事务配置类
 */
public class TransactionConfiguration {
    
    
    @Bean("transactionManager")
    public DataSourceTransactionManager getTransactionManager(DataSource dataSource){
    
    

        // 创建事务管理器对象
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();

        // 设置数据源
        transactionManager.setDataSource(dataSource);

        return transactionManager;
    }
}

14.5.3 主配置类

package cn.guardwhy.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * 主配置类步骤:
 *      1.使用@Configuration注解,声明主配置类
 *      2.使用@ComponentScan注解,扫描注解配置
 *      3.使用@Import注解,导入其它模块配置类
 *      4.使用@EnableTransactionManagement注解,启用spring对注解事务的配置
 */
@Configuration
@ComponentScan(value="cn.guardwhy")
@Import(value={
    
    DaoConfiguration.class,TransactionConfiguration.class})
@EnableTransactionManagement
public class SpringConfiguration {
    
    
}

14.5.4 表现层

  • Controller
package cn.guardwhy.controller;

import cn.guardwhy.config.SpringConfiguration;
import cn.guardwhy.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 账户表现层
 */
public class AccountController {
    
    

    public static void main(String[] args) {
    
    
        // 1.加载spring配置文件,创建spring ioc容器
        ApplicationContext context = new 
        AnnotationConfigApplicationContext(SpringConfiguration.class);

        // 2.获取账户service
        AccountService accountService = (AccountService)context.getBean("accountService");

        // 3.转账操作
        accountService.transfer("curry","james",200f);
    }
}

猜你喜欢

转载自blog.csdn.net/hxy1625309592/article/details/113557934