Spring JDBC及声明式事务

目录

Spring JDBC基础概念        

Spring声明式事务

 事务传播方式


Spring JDBC基础概念        

        Spring JDBC 封装了原生的JDBC API,使得处理关系型数据库更加简单。Spring JDBC的核心是JdbcTemplate,里面封装了大量数据库CRUD的操作。使用Spring JDBC有如下步骤:

1、pom增加依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.20.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.22.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

spring-context是spring的核心,负责容器中对象实例创建;spring-jdbc是对原生jdbc的封装;logback-classic主要为了数据库操作时辅助信息的日志输出。

2、IOC容器实例化数据源对象dataSource和JdbcTemplate对象

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="com.text"/>

    <bean id="dataSource"  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx:3306/test"></property>
        <property name="username" value="test"></property>
        <property name="password" value="test"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

 3、在各个DAO实现类中注入JdbcTemplate对象,通过JdbcTemplate的API实现数据库CRUD

JdbcTemplate的API重要的API:

  • jdbcTemplate.query* :各种查询方法
  • jdbcTemplate.update:各种新增、修改、删除等方法

示例:学生信息查询、新增

package com.text.dao.impl;

import com.text.dao.StudentDao;
import com.text.entity.Student;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

@Repository
public class StudentDaoImpl implements StudentDao {

    @Resource
    private JdbcTemplate jdbcTemplate;
    @Override
    public Student getById(String id) throws Exception {
        String sql = "select * from student where id = ?";
        Student student = jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(Student.class));
        return student;
    }

    @Override
    public void insert(Student student) throws Exception {
        String sql = "insert into student(id,name,age) values (?,?,?)";
        jdbcTemplate.update(sql,new Object[]{student.getId(),student.getName(),student.getAge()});
    }
}

Spring声明式事务

        上文的案例中,没有添加事务支持,如果在Service层封装了Dao层并且做了批量操作的动作,在批量操作时系统出现异常,会出现部分数据提交到数据库的情况,不满足数据同时提交、同时回滚的情况,参考代码如下:

1、Service服务类

package com.text.service;

import com.text.entity.Student;

public interface StudentService {
    void batchSave() throws Exception;
    Student searchById(String id) throws Exception;
}
package com.text.service.impl;

import com.text.dao.StudentDao;
import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class StudentServiceImpl implements StudentService {

    @Resource
    private StudentDao studentDao;

    @Override
    public void batchSave() throws Exception {
        for(int i=10;i<=20;i++) {
            if(i==15) {
                throw new RuntimeException("系统出现异常");
            }
            Student student = new Student(i+"","张三"+i,20);
            this.studentDao.insert(student);
        }
    }

    @Override
    public Student searchById(String id) throws Exception {
        return this.studentDao.getById(id);
    }
}

 2、测试类

package com.text;

import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        StudentService studentService = context.getBean("studentServiceImpl", StudentService.class);
        Student student = studentService.searchById("1");
        System.out.println(student);
        studentService.batchSave();
    }
}

3、运行结果分析:

 

从程序运行结果可以看出,程序循环插入10条数据时,程序每次都获取新的数据库连接,程序执行到第15条时,系统发生异常,前面10-14条数据提交到了数据库,不满足10条数据同时提交或同时回滚,通过配置事务管理器可以解决此问题。

        Spring声明式事务是针对编程式事务而言的,编程式事务就是在程序内部手工的编写Commit和Rollback方法进行事务的提交和回滚,编写相对麻烦,本文重点讨论基于注解声明式事务

        Spring声明式事务,实现的原理就是AOP的环绕通知,在程序全部执行正常后,自动提交事务,在程序出现异常时,自动回滚事务。实现基于注解Spring声明式事务,经过如下步骤:

1、配置事务管理器及开启注解形式的声明式事务

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="com.text"/>

    <bean id="dataSource"  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx:3306/test"></property>
        <property name="username" value="test"></property>
        <property name="password" value="test"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--事务管理器-->
    <bean id="transactionManager"     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 启用注解形式声明式事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

2、在需要事务的服务类上添加注解@Transactional

package com.text.service.impl;

import com.text.dao.StudentDao;
import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service
public class StudentServiceImpl implements StudentService {

    @Resource
    private StudentDao studentDao;

    @Transactional //添加事务支持
    @Override
    public void batchSave() throws Exception {
        for(int i=10;i<=20;i++) {
            if(i==15) {
                throw new RuntimeException("系统出现异常");
            }
            Student student = new Student(i+"","张三"+i,20);
            this.studentDao.insert(student);
        }
    }

    @Override
    public Student searchById(String id) throws Exception {
        return this.studentDao.getById(id);
    }
}

3、测试类(和上面一致),运行后结果:

     

从运行结果看出,由于service的批量插入方法上添加了事务支持,批量新增时,用的是同一个数据库连接,系统发生异常后,数据没有出现部分提交的情况。

 事务传播方式

        Spring事务的默认的事务传播特性是Required,即当前环境没有事务,则创建一个事务,如果已经在一个事务中,则加入这个事务中。几种事务传播特性如下:

  1. required  如果有事务正在运行,当前方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行;
  2. requires_new 总是主动开启事务;如果存在外层事务,就将外层事务挂起
  3. supports 如果不存在外层事务,就不开启事务;否则使用外层事务
  4. not_supported 当前的方法不应该运行在事务中,如果当前有运行的事务,将它挂起
  5. mandatory 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
  6. never 当前的方法不应该运行在事务中,如果运行在事务中就抛出异常
  7. nested 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行    

示例:requires_new 总是主动开启事务;如果存在外层事务,就将外层事务挂起

A方法调用了B和C,其中A,B和C采用的是requires_new 传播特性,则程序内部事务如下示意图:

上一篇:专业软件应用后评估方法


下一篇:C# winforms 使用菜单和右键菜单