SSM学习之路——spring第四天_使用xml实现事务控制

正常来说,我们写一个事务,通常是有commit和rollback两步,为最重要的两步,无异常产生则commit,有异常产生则rollback,这基本上是一个标准的步骤,那spring也知道这个,给我们提供了事务控制相关的配置,让我们更好的使用。
涉及到相关内容在用到的时候进行拓展

一、新建maven工程并在pom.xml添加依赖

	<!--打包方式为jar-->
	<packaging>jar</packaging>
	
    <dependencies>
        <!--spring框架的依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--spring aop的依赖-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <!--spring事务的依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--spring的junit的依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--spring和junit整合的依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--数据库依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!--spring的jdbc依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    </dependencies>

二、建立所需要的类

分别为dao层,service层和表现ui层,domain为账户实体类
在这里插入图片描述
Account类:
继承序列化接口,添加与数据库对应的字段,并为此生成set&get&toString方法
在这里插入图片描述
 
 
IAccountDao接口:
添加一些查询数据库的基本方法,这里只添加三个

public interface IAccountDao {
    Account findByName(String name);

    Account findById(Integer id);

    void updateAccount(Account account);
}

 
 
IAccountDaoImpl实现类:

package com.itheima.dao.impl;

import com.itheima.domain.Account;
import com.itheima.dao.IAccountDao;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

import java.util.List;

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    public Account findByName(String name) {
        List<Account > accounts =  super.getJdbcTemplate().query("select * from account where name=?",
                new BeanPropertyRowMapper<Account>(Account.class),
                name);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    public Account findById(Integer id) {
        List<Account > accounts =  super.getJdbcTemplate().query("select * from account where id=?",
                new BeanPropertyRowMapper<Account>(Account.class),
                id);
        if (accounts.isEmpty()){
            return null;
        }
        if (accounts.size() > 1){
            System.out.println("结果集不唯一");
            return null;
        }
        return accounts.get(0);
    }

    public void updateAccount(Account account) {
        super.getJdbcTemplate().update("update account set money = ?,name=? where id = ?",account.getMoney(),account.getName(),account.getId());
    }
}

上面这段代码出现了一个extends JdbcDaoSupport,这个是spring对Dao层的一个Jdbc的支持,我们可以点进这个类看看!ctrl+F3
这个类内置了对jdbcTemplate的一个初始化,所以我们知道,可以通过配置xml对其创建对象,并且注入dataSource数据源
而且可以看到,这里使用另一个Nullable

在这里插入图片描述
所以就有了如下这段bean.xml的配置,向spring的ioc添加jdbcTemplate对象,并注入数据源

	<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
	<!--配置jdbcTemplate 并注入dataSource-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置dataSource相关参数,连接数据库的信息-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/springdb?serverTimezone=UTC&amp;characterEncoding=utf8&amp;useUnicode=true"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

为什么要继承一个JdbcDaoSupport呢,因为如果有很多个dao接口的实现类,那我们就要在每一个实现类(后缀为impl的类)中,都要进行初始化一个jdbcTemplate,对其数据注入。如果单写成一个类(作用和如上贴出来的这个spring提供的类是一样的),再进行继承也是可以,但是spring已经提供给我恩了,所以我们只管直接拿来用就行了,而不用手动编码(而且也没写的比人家好)。

就拿super.getJdbcTemplate().query("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name);这一句来说 通过super.getJbcTemplate来得到一个jdbcTemplate对象,查类方法用query
这里引用浅析BeanPropertyRowMapper。清晰明了
 
 

IAccountService接口:
在业务层setvice层 增加一个transfer方法

public interface IAccountService {
        Account findByName(String name);

        Account findById(Integer id);
        
        void updateAccount(Account account);
        
        void transfer(String sourceName,String targetName,Double money);
}

AccountServiceImpl类:

import com.itheima.domain.Account;
import com.itheima.dao.IAccountDao;
import com.itheima.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("accountService")
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao;

    public Account findByName(String name) {
        return accountDao.findByName(name);
    }

    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }


    public void transfer(String sourceName, String targetName, Double money) {
        System.out.println("transfer...");
        Account source = accountDao.findByName(sourceName);
        Account target = accountDao.findByName(targetName);
        double newSourceMoney = source.getMoney() - money;
        //对source账户进行扣除操作的时候,要检查前是否足够
        if (newSourceMoney < 0){
            throw new RuntimeException("余额不足!");
        }else{
            source.setMoney(newSourceMoney);
        }
        double newTargetMoney = target.getMoney() + money;
        target.setMoney(newTargetMoney);
        accountDao.updateAccount(source);
        accountDao.updateAccount(target);
    }
}

三、编写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: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">
    <!--配置ioc容器的注解支持-->
    <context:component-scan base-package="com.itheima"></context:component-scan>
	
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <!--配置jdbcTemplate 并注入dataSource-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置dataSource相关参数,连接数据库的信息-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/springdb?serverTimezone=UTC&amp;characterEncoding=utf8&amp;useUnicode=true"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>


    spring中基于XML的声明式事务控制配置步骤
        1、配置事务管理器
        2、配置事务的通知
            此时我们需要导入事务中的约束,tx名称空间的约束,同时也需要sop的
            使用tx:advice 标签配置事务通知
                属性:
                    id:给事务一个唯一标识
                    transaction-manager:给事务通知提供一个事务管理器引用
        3、配置AOP中的通用切入点表达式
        4、建立事务通知切入点表达式的对应关系
        5、配置事务的属性
            是在事务的通知tx:advice标签的内部
    

    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    
        配置事务的属性
            isolation:用于指定事务的隔离级别,默认值是DEFAULT,表示使用数据库的默认隔离级别
            propagation:用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事务,增删改选择它,查询方法选择SUPPORTS
            timeout:用于指定事务的超时时间,只有查询方法才能为true,默认值为false,表示读写
            read-only:用于指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写
            rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不会滚,不配置表示默认值,表示任何异常都回滚
            no-rollback-for:用于指定一个异常,当发生该异常时,事务不回滚,产生其他异常时事务回滚,不配置表示默认,表示任何异常都回滚
        
        <tx:attributes>
            <!--这里设置转账事务 涉及到改,所以propagation选择REQUIRED 只读为false-->
            <tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
        </tx:attributes>
    </tx:advice>
</beans>

注释比较乱也比较详细,简单说一下就是
1、transactionManager里引用数据源dataSource
2、tx:advice引用事务管理器transactionManager
3、aop:config配置切面和切入方法
4、aop:advice 事务的通知配置引用tx:advice
5、通过tx:advice内部的tx:attributes标签,指定事务的方法名以及事务的属性

事务的方法名和属性通常用如下配置:

在这里插入图片描述
图片引自:spring中事务管理器配置

四、编写测试

public class Client {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        as.transfer("小红", "小黑", 1000d);
    }
}

运行前:
在这里插入图片描述
运行后:
在这里插入图片描述
可以看到转账成功了,但是如果我们再运行一次,注意,这里小红只有500了,如果再转1000,余额是不够的。我们测试看看
报了一个我们编码时候设置的RuntimeException,并显示余额不足(详见上面的AccountServiceImpl类的transfer方法)
在这里插入图片描述
数据库更新了没?
在这里插入图片描述
并没有更新,喜大普奔,说明这个出现了异常的事务被我们控制住了,spring真方便,奥利给!

发布了23 篇原创文章 · 获赞 0 · 访问量 592

猜你喜欢

转载自blog.csdn.net/SixthMagnitude/article/details/104197001
今日推荐