javaee之Spring4

本文深入讲解Spring中的事务控制,包括基于XML和注解的声明式事务控制,详细介绍了配置步骤及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前说到AccountDao需要继承JdbcDaoSupport这个类,那么现在来看一下这个类的内容

JdbcDaoSupport.java

package com.itheima.dao.impl;

/**
 * 此类用于抽取dao中的重复代码
*/

public class JdbcDaoSupport {

    private JdbcTemplate jdbcTemplate;

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

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }


    public void setDataSource(DataSource dataSource) {
        if(jdbcTemplate == null){
            jdbcTemplate = createJdbcTemplate(dataSource);
        }
    }

    private JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
}
 

 

 

看一下bean.xml的配置

那么这个bean.xml还可以这样配置,既然有JbdcTemplate对象还有DataSource对象,那么就也可以注入DataSource去创建一个JdbcTemplate对象

 

 

 为什么说要这样配置呢,就是因为在我们以后的开发中,不止有一个Dao文件,可能会有很多的Dao文件,那么这些文件,最后都要去操作数据库,也就是说,这些文件都必须有一个JdbcTemplate对象和数据源,注意这里的数据源是封装到JdbcTemplate模板上面的

实际业务操作

但是你能想到的事儿,Spring也能做到,那么Spring怎么来做的

换句话说,Spring内部也是提供了那么一个JdbcSupport来供我们使用,这样也就是Spring给我们提供的一个jar包,内部封装好了JdbcTemplate和DataSource对象,我们就可以通过Bean.xml来进行配置,但是注解去配置不行,因为Spring给我们提供的jar是只读的

Spring中的事务控制

1.之前做了一个银行转账事务,现在我们通过xml来配置一下这个事务的实现过程

现在的要求就是,我们不要下面这个文件,也就是说我们不需要这个代理工厂去给我们生产一个代理对象了

BeanFactory.java

package com.pxx.factory;

import com.pxx.service.IAccountService;
import com.pxx.utils.TransactionManager;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class BeanFactory {
    private IAccountService accountService;

    private TransactionManager txManager;

    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }


    public final void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }

    /**
     * 把service里面的方法全部进行增强
     */
    public IAccountService getInstance() {

        return (IAccountService)Enhancer.create(accountService.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //这里面就是具体增强代码
                try {
                    System.out.println("开始增强了");
                    Object value = null;
                    //主要是我们需要在执行一个方法前后添加事务控制
                    txManager.beginTransaction();//开启事务
                    value = method.invoke(accountService,args);//有啥参数就拿过来匹配
                    //提交事务
                    txManager.commit();
                    System.out.println("增强结束");
                    return value;
                } catch (Exception e) {
                    //有异常出现,把数据进行回滚
                    txManager.rollback();;
                    System.out.println("出现饿了异常");
                    throw new RuntimeException(e);
                } finally {
                    //释放连接
                    txManager.release();
                }
            }
        });

    }
}

那么这个文件删除之后

 在bean.xml文件中,也会报错

 现在要求就是通过配置Spring,调用自己的Servcie来完成代码的编写工作

既然我们要通过AOP来进行一个事务控制,就必修在xml头部引入AOP的约束

 配好了之后,检查一下pom.xml是否有如下这个依赖

这个依赖没有,是不能去解析切入点表达式的

 正常测试没问题,钱不会少

下面把上面的过程全部改成注解的AOP实现以及问题分析

既然要用到注解配置,那么bean.xml约束就要从新去修改一下,要引入context部分的内容

 

然后我们去修改service层

 

 下面我们去配置一下Dao层的注解

 注意QueryRunner这个类里面又有一个数据源的依赖,但是这个数据源又是依赖于配置文件的所以下面的不能删除

 下面去配置一下ConnectionUtils这个类

 下面我们去配置一下事务管理器

package com.pxx.utils;

import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 *
 * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
 */
@Component("txManger")
@Aspect
public class TransactionManager {

    //引入一个连接对象
    @Autowired
    private ConnectionUtils connectionUtils;



   /* //我们要采用set注入,必须添加一个set方法
    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }*/

   //配置一个切入点
    @Pointcut("execution(* com.pxx.service.impl.*.*(..))")
    private void pt1(){}

    //开启事务
    @Before("pt1()")
    public void beginTransaction() {
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    @AfterReturning("pt1()")
    public  void commit(){
        try {
            System.out.println("事务提交");
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    @AfterThrowing("pt1()")
    public  void rollback(){
        try {
            System.out.println("事务回滚");
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 释放连接
     */
    @After("pt1()")
    public  void release(){
        try {
            connectionUtils.getThreadConnection().close();//还回连接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 配置bean.xml对事务的支持

 

测试一下代码

首先看一下正常转账能不能行

按照上面单独这种配置,会出现这种报错

 这里涉及到一个问题,Spring的通知执行流程就是:

那如果我们要解决这个问题,我们就去配置一个环绕通知,让每一个通知明确知道在何时何地的为我们服务

 

完整代码

 TransactionManager.java
package com.pxx.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 *
 * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
 */
@Component("txManger")
@Aspect
public class TransactionManager {

    //引入一个连接对象
    @Autowired
    private ConnectionUtils connectionUtils;



   /* //我们要采用set注入,必须添加一个set方法
    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }*/

   //配置一个切入点
    @Pointcut("execution(* com.pxx.service.impl.*.*(..))")
    private void pt1(){}

    //开启事务
   // @Before("pt1()")
    public void beginTransaction() {
        try {
            System.out.println("事务开启");
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
   // @AfterReturning("pt1()")
    public  void commit(){
        try {
            System.out.println("事务提交");
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
   // @AfterThrowing("pt1()")
    public  void rollback(){
        try {
            System.out.println("事务回滚");
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 释放连接
     */
   // @After("pt1()")
    public  void release(){
        try {
            System.out.println("事务关闭");
            connectionUtils.getThreadConnection().close();//还回连接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Around("pt1()")
    public Object aroundAdvice(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            //1.获取参数
            Object[] args = pjp.getArgs();
            //2.开启事务
            this.beginTransaction();
            //3.执行方法
            rtValue = pjp.proceed(args);
            //4.提交事务
            this.commit();

            //返回结果
            return  rtValue;

        }catch (Throwable e){
            //5.回滚事务
            this.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.释放资源
            this.release();
        }
    }
}

这样就是正常执行业务流程 

 Spring中事务控制的一组API

 

 

 

 

 

 

 

 

基于 XML 的声明式事务控制(配置方式) 

 先来看一下AccountDaoImpl

 

 看service层

 

 这个里面还没有配任何事务,下面去看一下测试方法

测试一下这个方法

 

 

 下面来说一下Spring基于xml的声明式事务控制

 spring中基于XML的声明式事务控制配置步骤
        1、配置事务管理器


        2、配置事务的通知
                此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的

                
           

 

    使用tx:advice标签配置事务通知
                    属性:
                        id:给事务通知起一个唯一标识
                        transaction-manager:给事务通知提供一个事务管理器引用

事务属性的一些配置信息


             3、配置AOP中的通用切入点表达式

        这个也就是说把这个事务引入到哪一个包里面的方法中,aop就相当于把一个事务类插进去,然后利用动态代理进行增强
        4、建立事务通知和切入点表达式的对应关系
        5、配置事务的属性
               是在事务的通知tx:advice标签的内部

 下面上一下全部的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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 配置业务层-->
    <bean id="accountService" class="com.pxx.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!-- 配置账户的持久层-->
    <bean id="accountDao" class="com.pxx.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


    <!-- 配置数据源-->
    <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/test"></property>
        <property name="username" value="root"></property>
        <property name="password" value="5201314"></property>
    </bean>

    <!--
    配置事务管理器
    -->
    <bean id="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="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!--配置aop-->
    <aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* com.pxx.service.impl.*.*(..))"/>
        <!--建立切入点与事务通知的对应关系-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

</beans>

 下面我们来测试一下正常业务转账

出现异常能不能被事务控制

 

 异常有了

数据也没有转成功

Spring基于注解的声明式事务控制

 1.一般要配置注解,就要进入context的xml约束

我们直接在原xml上修改

 

现在就可以注解了,先来配置service

 下面就可以不要了

 

 

所以上面这个AccountDaoImpl不能去继承JdbcDaoSupport

 

 

 想要配置成功,还要配置创建容器时需要扫描的包

下面就要开始配置事务了,下面说一下Spring基于注解的声明式事务控制步骤

 

 第一步:

 第二步:

引入事务管理器 

 

 第三步:

 这个属性配置好像确实不如注解方便

这个时候去测试一下

异常报了,去看一下数据

控制住了。

 Spring基于纯注解的声明式事务控制

上面还是用到部分xml配置

因为没有xml配置文件,所以我们要写一个配置类来代替xml文件

 然后上一个数据库连接的配置文件

下面看一下JdbcConifg.java

package config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

/**
 * 和连接数据库相关的配置类
 */
public class JdbcConfig {

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    /**
     * 创建JdbcTemplate
     * @param dataSource
     * @return
     */
    @Bean(name="jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    /**
     * 创建数据源对象
     * @return
     */
    @Bean(name="dataSource")
    public DataSource createDataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

在注解上大部分相关配置就配好了 

下面我们就要去配置事务注解的支持

 

下面去配置一下事务管理器

下面又要去创建一个配置类

然后重新改一下配置文件

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值