Java框架-Spring的jdbc、连接池及事务管理

1. Spring的AOP编程

1.1 纯xml编程

<!--通知配置类型-->
<aop:config>
    <!--设置切面-->
    <aop:aspect ref="logger">
        <!--设置切入点表达式-->
        <aop:pointcut id="pt" expression="execution(* com.azure..*ServiceImpl.*(..))"></aop:pointcut>
        <!--设置通知-->
        <!--前置通知-->
        <aop:before method="beforeLog" pointcut-ref="pt"></aop:before>
        <!--后置通知-->
        <aop:after-returning method="afterReturningLog" pointcut-ref="pt"></aop:after-returning>
        <!--异常通知-->
        <aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing>
        <!--最终通知-->
        <aop:after method="after" pointcut-ref="pt"></aop:after>
    </aop:aspect>
</aop:config>

<!--通知配置类型-->
<aop:config>
    <!--设置切面-->
    <aop:aspect ref="logger">
        <!--设置切入点表达式-->
        <aop:pointcut id="pt" expression="execution(* com.azure..*ServiceImpl.*(..))"></aop:pointcut>
        <!--设置通知-->
        <!--环绕通知-->
		<aop:around method="around" pointcut-ref="pt"></aop:around>
    </aop:aspect>
</aop:config>

1.2 结合xml和注解编程

  • 需求:service层实现事务管理,包括事务执行、事务提交,事务回滚。建议使用环绕通知

1.2.1 创建项目、添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.azure</groupId>
    <artifactId>day54projects_spring_AOP</artifactId>
    <version>1.0-SNAPSHOT</version>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.7</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
</dependencies>
    
</project>

1.2.2 service

1.2.2.1 接口
public interface IAccountService {
    /*模拟保存账户功能*/
    void save();
    String update(int id);
}
1.2.2.2 实现类
public class AccountServiceImpl implements IAccountService {
    @Override
    public void save() {
        System.out.println("执行保存方法!");
    }

    @Override
    public String update(int id) {
        System.out.println("方法参数:"+id);
        return "Service本身方法返回值";
    }
}

1.2.4 事务切面类

/**
 * 事务切面类
 */
@Aspect //指定当前类为切面类
@Component  //创建切面类对象
public class TransactionManager {

    //定义切点表达式
    @Pointcut("execution(* com.azure.service.*.*(..))")
    public void pt(){}

    /*
    // 【前置通知】
    @Before("pt()")
    public void beginTransaction(){
        System.out.println("【前置通知】 在执行目标方法之前执行");
    }

    //【后置通知(返回后通知)】
    @AfterReturning("pt()")
    public void afterReturning(){
        System.out.println("【后置通知】执行目标方法正常结束执行");
    }

    //【异常通知】
    @AfterThrowing("pt()")
    public void afterThrowing(){
        System.out.println("【异常通知】 执行目标方法出现异常时候执行");
    }

    //【最终通知】
    @After("pt()")
    public void after() {
        System.out.println("【最终通知】在调用目标对象方法后始终执行");
    }
    */

    // 环绕目标对象方法执行
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            System.out.println("[环绕通知] 环绕前");
            //获取参数
            Object[] args = pjp.getArgs();
            //修改参数
            args[0] = 898;
            //执行方法
            Object retV = pjp.proceed(args);
            System.out.println("[环绕通知] 环绕后");
            //返回方法执行结果,还能修改结果
            return retV + "~~~想不到吧";
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("[环绕通知] 环绕异常");
            return null;
        } finally {
            System.out.println("[环绕通知] 环绕最终");
        }
    }
}

1.2.5 测试类

public class TestApp {
    public static void main(String[] args) {
        //创建对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //因为service实现接口,故采用jdk动态代理。必须用接口接收。如果用实现类接收会报错!
        IAccountService service = (IAccountService) ac.getBean("accountServiceImpl");
        //class com.sun.proxy.$Proxy18
        System.out.println(service.getClass());
        String result = service.update(999);
        System.out.println(result);
    }
}

1.3 纯注解编程

  • 需求:改造上述案例代码,使用纯注解实现

1.3.1 配置管理类

/**
 * 配置管理类
 * 1.@Configuration     指定该类为配置管理类,创建容器时候只要加载当前类字节码就可以?
 * 2.@ComponentScans    开启注解扫描,可指定多个@ComponentScan,实现扫描多个包
 *                          注解多个包的时候,要把所有包放在{}中,每个包之间用逗号分隔
 * 3.@ComponentScan     开启注解扫描,扫描一个包(子包)
 * 4.@EnableAspectJAutoProxy  开启AOP自动代理
 */
@Configuration
@ComponentScans({
        @ComponentScan(basePackages = "com.azure.service"),
        @ComponentScan(basePackages = "com.azure.utils")
})
@EnableAspectJAutoProxy
public class SpringConfiguration {
}

1.3.2 测试类

@RunWith(SpringJUnit4ClassRunner.class)//使用spring的junit
@ContextConfiguration(classes = SpringConfiguration.class)	//加载配置类
public class TestApp2 {
    //从容器中获取对象
    @Autowired
    private IAccountService service;

    @Test
    public void test() {
        System.out.println(service.getClass());
        String result = service.update(999);
        System.out.println(result);
    }
}

2. 事务回顾

2.1 事务概念

  • 事务指一组执行单元,该单元的各个组成部分要不同时执行成功,要不同时执行失败。

2.2 事务特性ACID

  • A-原子性(Automicity):事务是不可再分割的最小工作单位;
  • C-一致性(Consistency):事务必须是数据库状态从一个一致性状态,转变成另外一个一致性状态。类似能量守恒定律
  • I-隔离性(Isolation):一个事务的执行不能受到其它事务的影响。事务间相互独立。
  • D-持久性(Durability):一个事务一旦提交,则它对数据库中的数据的改变将会永久存储

3. Spring声明式事务管理

  • 声明式事务管理式作用于业务层的事务处理
  • Spring的事务管理控制是基于AOP的,可以使用编程方式也可使用配置方式。

3.1 String事务控制的API

  • PlatformTransactionManager接口是Spring的事务管理器,提供获取事务状态信息、提交事务、回滚事务的方法。
  • 实际开发中使用其实现类
  1. 使用 SpringJDBCiBatis 进行持久化数据时使用:
    org.springframework.jdbc.datasource.DataSourceTransactionManager

  2. Hibernate 版本进行持久化数据时使用org.springframework.orm.hibernate5.HibernateTransactionManager

3.1.1 TransactionDefinition

  • 事务的定义信息对象

  • 包含以下方法:

    - String getName()		获取事务对象名称
    - int getIsolationLevel()	获取事务隔离级
    - int getPropagationBehavior()	获取事务传播行为
    - int getTimeout()	获取事务超时时间
    - boolean isReadOnly()	获取事务是否只读
    
  • 事务的传播行为有7种,常用的有以下三种

    REQUIRED 
    1. 默认值
    2. 表示当前执行方法必须有事务环境
    3. 如果当前执行方法没有事务环境,则创建新的事务;如果当前执行方法有事务环境,则加入当前事务,就不创建新的事务。
    4. 应用:添加、修改、删除需要指定事务的传播行为是REQUIRED
    
    SUPPORTS
    1. 支持事务
    2. 当前执行方法有事务环境则支持,没有事务环境也可以运行。事务可有可无。
    3. 应用:查询
    
    REQUERS_NEW
    1. 表示当前执行方法必须有事务环境
    2. 不管当前方法有没有事务环境,都会创建一个新的事务。
    
  • 超时时间

    默认值是-1,没有超时限制。如果有,以秒为单位进行设置。

  • 是否是只读事务

    增删改是读写事务,查询设置为只读

3.1.2 TransactionStatus

  • 事务具体的运行状态,提交事务和回滚事务的方法需要传入此参数

3.2 xml配置方式实现事务管理(重要)

  • 作用于service层
  • 实现事务代码与业务代码完全解耦,或者说完全分离

下面以service层调用dao层方法保存用户为例,如果保存两个用户的方法之间存在错误代码,而应用事务之后数据库没有变化,说明事务有生效

3.2.0 创建表格

  • 此处省略

3.2.1创建项目,添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.azure</groupId>
    <artifactId>day55projects_spring_tx_xml</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--spring核心包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--aop支持包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
        <!--事务支持包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--mysql驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>
        <!--数据库连接池c3p0-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <!--jdbctemplate-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--junit支持包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>
</project>

3.2.2 实体类

public class Account {
    private int accountId;
    private int uid;
    private double money;
    /*省略*/
}

3.2.3 dao层

3.2.3.1 dao接口
public interface IAccountDao {
    /*保存账户*/
    void save(Account account);
}
3.2.3.2 dao接口
@Repository
public class AccountDaoImpl implements IAccountDao {

    /*使用注解获取对象*/
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void save(Account account) {
        jdbcTemplate.update("insert into account values (null,?,?)",account.getUid(),account.getMoney());
    }
}

3.2.4 service层

3.2.4.1 service接口
public interface IAccountService {
    /*保存账户*/
    void save(Account account);
}
3.2.4.2 service实现类
@Service
public class AccountServiceImpl implements IAccountService {

    /*使用注解获取dao对象*/
    @Autowired
    private IAccountDao accountDao;

    @Override
    public void save(Account account) {
        /*模拟事务*/
        accountDao.save(account);
        int i = 1/0;
        accountDao.save(account);
    }
}

如果注释掉int i = 1/0;,数据库插入两条数据,而不注释该行代码数据库无变动,说明事务控制已生效

3.2.5 bean.xml配置

思路:

  1. 需要进行数据库操作,所以要加载数据库连接池,而加载数据库连接池又要先加载数据库配置文件;

  2. 需要进行数据库操作,所以要加载jdbcTemplate;

  3. 因为使用到注解配置,所以要开启注解扫描;

  4. 使用事务管理,首先要配置事务管理类(切面类),并注入连接池

    然后配置事务通知规则,拦截哪些方法,对这些方法进行什么管理(读写?只读?)

    因为事务的原理是AOP,所以要配置AOP,里面包含切入点表达式,还有重要的一点,将事务通知规则与切入点表达式建立起关系

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

    <!--0.加载数据库配置文件,context标签-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--0.开启注解扫描-->
    <context:component-scan base-package="com.azure"></context:component-scan>

    <!--1.配置数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--2.配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3.配置声明式事务-->
    <!--3.1配置事务管理器(事务切面类)-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3.2事务通知规则(拦截到指定的方法后如何管理事务:读写、只读),tx标签-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!--*表示所有方法,运行时必须有事务环境,且是读写事务-->
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <!--3.3AOP配置:监理切入点表达式与事务通知规则的关系,aop标签-->
    <aop:config>
        <!--设置切入点表达式-->
        <aop:pointcut id="pt" expression="execution(* com.azure..*ServiceImpl.*(..))"></aop:pointcut>
        <!--建立关联-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>

</beans>

3.2.6 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean.xml")	//加载配置类
public class TestApp {
    /*从容器中获取并注入对象*/
    @Autowired
    private IAccountService accountService;

    @Test
    public void save(){
        Account account = new Account();
        account.setUid(46);
        account.setMoney(100100);
        //保存用户
        accountService.save(account);
    }
}

3.2.7 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:aop="http://www.springframework.org/schema/aop"
       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/util http://www.springframework.org/schema/util/spring-util.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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--0.加载数据库配置文件,context标签-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--0.开启注解扫描-->
    <context:component-scan base-package="com.azure"></context:component-scan>

    <!--1.配置数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--2.配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3.配置声明式事务-->
    <!--3.1配置事务管理器(事务切面类)-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3.2事务通知规则(拦截到指定的方法后如何管理事务:读写、只读),tx标签-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!--对查询的方法进行拦截,事务环境可有可无,只读事务-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="search*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>
            <!--注意,*号语句必须放在最下方,否则所有的方法都会被拦截而无法执行查询的规则-->
            <!--*表示所有方法,运行时必须有事务环境,且是读写事务-->
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <!--3.3AOP配置:监理切入点表达式与事务通知规则的关系,aop标签-->
    <aop:config>
        <!--设置切入点表达式-->
        <aop:pointcut id="pt" expression="execution(* com.azure..*ServiceImpl.*(..))"></aop:pointcut>
        <!--建立关联-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>

</beans>

逻辑图:

在这里插入图片描述

3.3 注解方式实现事务管理(重要)

  • 在bean.xml配置中使用<tx:annotation-driven transaction-manager="txManager"/>开启声明式事务注解支持,就可以在业务实现类使用@Transactional注解配置信息

3.3.1 bean.xml改造开启事务注解

    <!--0.加载数据库配置文件,context标签-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--0.开启注解扫描-->
    <context:component-scan base-package="com.azure"></context:component-scan>

    <!--1.配置数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--2.配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3.配置声明式事务-->
    <!--3.1配置事务管理器(事务切面类)-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3.2开启声明式事务注解支持-->
    <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>

3.3.2 service实现类改造

/**
 * @Transactional
 * 1.Spring声明式事务控制注解
 * 2.定义到实现类上,表示当前实现类的所有方法都进行事务控制
 * 3.定义到实现类的方法上,表示当前方法应用事务
 * 4.定义到接口上,表示接口的所有实现类都应用事务
 * 5.定义到接口方法上,表示实现该接口的所有实现类实现此方法时候自动应用事务。
 * 6.属性说明
 *   readOnly = false  默认值,表示读写的事务:增删改操作
 *              true   只读事务,只能做查询。
 *   propagation = Propagation.REQUIRED 默认值;
 *                     表示当前运行方法必须有事务环境。
 *   isolation = Isolation.DEFAULT 数据事务的隔离级别。
 *   timeout = -1       表示事务超时时间,指定为-1表示不设置超时时间,
 *                      由数据库来决定超时时间
 *   noRollbackFor = ArithmeticException.class
 *                      遇到指定的异常不回滚。
 */
@Service
@Transactional(
        readOnly = false,
        propagation = Propagation.REQUIRED,
        isolation = Isolation.DEFAULT,
        timeout = -1
)
public class AccountServiceImpl_anno implements IAccountService {

    /*使用注解获取dao对象*/
    @Autowired
    private IAccountDao accountDao;

    @Override
    public void save(Account account) {
        /*模拟事务*/
        accountDao.save(account);
        int i = 1/0;
        accountDao.save(account);
    }
}

3.3.3 测试类

/**
 * 使用spring的junit测试
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean_anno.xml")
public class TestApp_anno {
    /*从容器中获取并注入对象*/
    @Qualifier("accountServiceImpl_anno")   //因为本模块下有两个IAccountService的实现类,所以要用@Qualifier指定注入的对象
    @Autowired
    private IAccountService accountService;

    @Test
    public void save(){
        Account account = new Account();
        account.setUid(46);
        account.setMoney(100100);
        //保存用户
        accountService.save(account);
    }
}

3.4 纯注解方式实现事务管理

思路:xml转纯注解的套路,将xml的语句转化成配置类,在配置类上使用注解进行配置

3.4.1 配置管理类SpringConfiguration

/**
 * @Configuration                设置为配置类,容器开启时记载此注解的类
 * @ComponentScan("com.azure")   开启注解扫描
 * @EnableTransactionManagement  开启声明式事务管理,可以使用@Transactional注解
 * @Import(JdbcConfig.class)     加载其他配置管理类
 */
@Configuration
@ComponentScan("com.azure")
@EnableTransactionManagement
@Import(JdbcConfig.class)
public class SpringConfiguration {
}

3.4.2 配置JdbcConfig管理类

//加载jdbc.properties配置文件
@PropertySource("jdbc.properties")
public class JdbcConfig {
    // 通过@Value获取配置文件值
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    /**
     * 1.创建连接池,加入ioc容器
     * @Bean 会自动把方法返回的结果加入ioc容器
     *       name属性指定加入ioc容器的对象的名称
     */
    @Bean(name = "dataSource")
    public DataSource createDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driver);
        ds.setJdbcUrl(url);
        ds.setUser(username);
        ds.setPassword(password);
        return ds;
    }

    /**
     * 2.创建JdbcTemplate,加入ioc容器
     * @Bean 修饰的方法的参数:
     * 1.会自动根据参数类型去容器中对象注入;
     * 2.如果类型有多个,就根据形参名称取容器找该名称对应的对象注入。
     * 3.如果想根据指定的名称去容器中找对象,注入到方法的形参上,使用@Qualifier注解
     *   @Qualifier("dataSource") 去容器找名称是“dataSource”的对象注入方法形参。
     */

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(@Qualifier("dataSource") DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    /**
     * 3. 创建事务管理器
     */
    @Bean(name = "txManager")
    public DataSourceTransactionManager createDataSourceTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

3.4.3 测试类

/**
 * 使用spring的junit测试
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)     //加载配置类
public class TestApp {
    /*从容器中获取并注入对象*/
    @Autowired
    private IAccountService accountService;

    @Test
    public void save(){
        System.out.println(accountService.getClass());
        Account account = new Account();
        account.setUid(46);
        account.setMoney(100100);
        //保存用户
        accountService.save(account);
    }
}

4. 总结

一、Spring声明式事务控制

1)什么是声明式事务?

    因为每个项目,每个数据访问层实现技术,都要涉及事务控制。所以,spring已经提供了对不同的数据库实现技术的不同事务控制实现。
  • PlatformTransactionManager 接口
    • DataSourceTransactionManager 连接池中事务控制实现
    • JpaTransactionManager jpa中事务控制支持
    • HibernateTransactionManager 对hibernate的事务控制支持

2)如何实现实现spring声明式事务?

  1. XML配置 事务管理器、通知规则、Aop配置
  2. 注解实现 @Transacrtional 注解
  3. 纯注解实现 @EnableTransactionManagement

二、Spring声明式事务, 原理

Spring声明式事务原理----> AOP---->动态代理

三、Spring声明式事务的优缺点

  1. 优点

    • 任何项目都要用事务,spring提供了事务控制统一实现,减少开发中事务控制的代码
    • 解耦:业务代码与事务控制代码完全分离,最解耦的设计实现。
  2. 缺点

    • Spring声明式事务控制,是粗粒度的事务控制。方法级别的事务控制,不能对方法的某几行进行事务控制。

四、细粒度的事务控制

可以对方法的任意几行进行事务控制。

如何实现细粒度事务控制? 自己写代码控制事务。

(Spring提供的编程式事务控制可以解决:细粒度事务控制问题。)

5. 编程方式实现事务管理

  • 使用模板

    在这里插入图片描述

5.1 改造纯注解案例,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/util http://www.springframework.org/schema/util/spring-util.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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--0.加载数据库配置文件,context标签-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--0.开启注解扫描-->
    <context:component-scan base-package="com.azure"></context:component-scan>

    <!--1.配置数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--2.配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3.配置声明式事务-->
    <!--3.1配置事务管理器(事务切面类)-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3.2编程方式实现事务管理-->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" >
        <property name="transactionManager" ref="txManager"></property>
    </bean>
</beans>

5.2 改造service实现类

@Service
public class AccountServiceImpl_tsTemplate implements IAccountService {

    /*使用注解获取dao对象*/
    @Autowired
    private IAccountDao accountDao;

    //创建编程事务模板
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Override
    public void save(Account account) {
        //套用模板
        TransactionCallback<Object> callback = new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                /*模拟事务*/
                accountDao.save(account);
                int i = 1/0;
                accountDao.save(account);
                return null;
            }
        };
        //编程式事务控制
        transactionTemplate.execute(callback);
    }
}

5.3 改造测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean_template.xml")     //加载配置类
public class TestApp_tsTemplate {
    /*从容器中获取并注入对象*/
    @Qualifier("accountServiceImpl_tsTemplate")
    @Autowired
    private IAccountService accountService;

    @Test
    public void save(){
        System.out.println(accountService.getClass());
        Account account = new Account();
        account.setUid(46);
        account.setMoney(100100);
        //保存用户
        accountService.save(account);
    }
}

猜你喜欢

转载自blog.csdn.net/KeepStruggling/article/details/83450048