• 通过注解实现Spring 声明式事务管理


    小Alan接着上一篇Spring事务管理入门与进阶做一些补充,如果对Spring事务管理还不了解的可以看看上一篇文章。

    实例

    在我们开始之前,至少有两个数据库表是至关重要的,在事务的帮助下,我们可以实现各种 CRUD 操作。以 Student 表为例,该表是使用下述 DDL 在 MySQL TEST 数据库中创建的。

    1 CREATE TABLE Student(
    2    ID   INT NOT NULL AUTO_INCREMENT,
    3    NAME VARCHAR(20) NOT NULL,
    4    AGE  INT NOT NULL,
    5    PRIMARY KEY (ID)
    6 );

    第二个表是 Marks,我们用来存储基于年份的学生标记。在这里,SID 是 Student 表的外键。

    1 CREATE TABLE Marks(
    2    SID INT NOT NULL,
    3    MARKS  INT NOT NULL,
    4    YEAR   INT NOT NULL
    5 );

    现在让我们编写 Spring JDBC 应用程序来在 Student 和 Marks 表中实现简单的操作。让我们适当的使用 Eclipse IDE,并按照如下所示的步骤来创建一个 Spring 应用程序:

    步骤一:创建一个名为 SpringExample 的项目,并在创建的项目中的 src 文件夹下创建包 com.tutorialspoint

    步骤二:使用 Add JARs 选项添加必需的 Spring 库。

    步骤三:在项目中添加其它必需的库 mysql-connector-java.jar,org.springframework.jdbc.jar 和 org.springframework.transaction.jar。如果你还没有这些库,你可以下载它们。

    步骤四:创建 DAO 接口 StudentDAO 并列出所有需要的方法。尽管它不是必需的并且你可以直接编写 StudentJDBCTemplate 类,但是作为一个好的实践,我们还是做吧。

    步骤五:在 com.tutorialspoint 包下创建其他必需的 Java 类 StudentMarksStudentMarksMapperStudentJDBCTemplate 和 MainApp。如果需要的话,你可以创建其他的 POJO 类。

    步骤六:确保你已经在 TEST 数据库中创建了 Student 和 Marks 表。还要确保你的 MySQL 服务器运行正常并且你使用给出的用户名和密码可以读/写访问数据库。

    步骤七:在 src 文件夹下创建 Beans 配置文件 Beans.xml 。

    最后一步:最后一步是创建所有 Java 文件和 Bean 配置文件的内容并按照如下所示的方法运行应用程序。

    下面是数据访问对象接口文件 StudentDAO.java 的内容:

     1 package com.tutorialspoint;
     2 import java.util.List;
     3 import javax.sql.DataSource;
     4 public interface StudentDAO {
     5    /** 
     6     * This is the method to be used to initialize
     7     * database resources ie. connection.
     8     */
     9    public void setDataSource(DataSource ds);
    10    /** 
    11     * This is the method to be used to create
    12     * a record in the Student and Marks tables.
    13     */
    14    public void create(String name, Integer age, Integer marks, Integer year);
    15    /** 
    16     * This is the method to be used to list down
    17     * all the records from the Student and Marks tables.
    18     */
    19    public List<StudentMarks> listStudents();
    20 }

    以下是 StudentMarks.java 文件的内容:

     1 package com.tutorialspoint;
     2 public class StudentMarks {
     3    private Integer age;
     4    private String name;
     5    private Integer id;
     6    private Integer marks;
     7    private Integer year;
     8    private Integer sid;
     9    public void setAge(Integer age) {
    10       this.age = age;
    11    }
    12    public Integer getAge() {
    13       return age;
    14    }
    15    public void setName(String name) {
    16       this.name = name;
    17    }
    18    public String getName() {
    19       return name;
    20    }
    21    public void setId(Integer id) {
    22       this.id = id;
    23    }
    24    public Integer getId() {
    25       return id;
    26    }
    27    public void setMarks(Integer marks) {
    28       this.marks = marks;
    29    }
    30    public Integer getMarks() {
    31       return marks;
    32    }
    33    public void setYear(Integer year) {
    34       this.year = year;
    35    }
    36    public Integer getYear() {
    37       return year;
    38    }
    39    public void setSid(Integer sid) {
    40       this.sid = sid;
    41    }
    42    public Integer getSid() {
    43       return sid;
    44    }
    45 }

    下面是 StudentMarksMapper.java 文件的内容:

     1 package com.tutorialspoint;
     2 import java.sql.ResultSet;
     3 import java.sql.SQLException;
     4 import org.springframework.jdbc.core.RowMapper;
     5 public class StudentMarksMapper implements RowMapper<StudentMarks> {
     6    public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {
     7       StudentMarks studentMarks = new StudentMarks();
     8       studentMarks.setId(rs.getInt("id"));
     9       studentMarks.setName(rs.getString("name"));
    10       studentMarks.setAge(rs.getInt("age"));
    11       studentMarks.setSid(rs.getInt("sid"));
    12       studentMarks.setMarks(rs.getInt("marks"));
    13       studentMarks.setYear(rs.getInt("year"));
    14       return studentMarks;
    15    }
    16 }

    下面是定义的 DAO 接口 StudentDAO 实现类文件 StudentJDBCTemplate.java:

     1 package com.tutorialspoint;
     2 import java.util.List;
     3 import javax.sql.DataSource;
     4 import org.springframework.dao.DataAccessException;
     5 import org.springframework.jdbc.core.JdbcTemplate;
     6 public class StudentJDBCTemplate implements StudentDAO{
     7    private JdbcTemplate jdbcTemplateObject;
     8    public void setDataSource(DataSource dataSource) {
     9       this.jdbcTemplateObject = new JdbcTemplate(dataSource);
    10    }
    11    public void create(String name, Integer age, Integer marks, Integer year){
    12       try {
    13          String SQL1 = "insert into Student (name, age) values (?, ?)";
    14          jdbcTemplateObject.update( SQL1, name, age);
    15          // Get the latest student id to be used in Marks table
    16          String SQL2 = "select max(id) from Student";
    17          int sid = jdbcTemplateObject.queryForInt( SQL2 );
    18          String SQL3 = "insert into Marks(sid, marks, year) " + 
    19                        "values (?, ?, ?)";
    20          jdbcTemplateObject.update( SQL3, sid, marks, year);
    21          System.out.println("Created Name = " + name + ", Age = " + age);
    22          // to simulate the exception.
    23          throw new RuntimeException("simulate Error condition") ;
    24       } catch (DataAccessException e) {
    25          System.out.println("Error in creating record, rolling back");
    26          throw e;
    27       }
    28    }
    29    public List<StudentMarks> listStudents() {
    30       String SQL = "select * from Student, Marks where Student.id=Marks.sid";
    31       List <StudentMarks> studentMarks=jdbcTemplateObject.query(SQL, 
    32       new StudentMarksMapper());
    33       return studentMarks;
    34    }
    35 }

    现在让我们改变主应用程序文件 MainApp.java,如下所示:

     1 package com.tutorialspoint;
     2 import java.util.List;
     3 import org.springframework.context.ApplicationContext;
     4 import org.springframework.context.support.ClassPathXmlApplicationContext;
     5 public class MainApp {
     6    public static void main(String[] args) {
     7       ApplicationContext context = 
     8              new ClassPathXmlApplicationContext("Beans.xml");
     9       StudentDAO studentJDBCTemplate = 
    10       (StudentDAO)context.getBean("studentJDBCTemplate");     
    11       System.out.println("------Records creation--------" );
    12       studentJDBCTemplate.create("Zara", 11, 99, 2010);
    13       studentJDBCTemplate.create("Nuha", 20, 97, 2010);
    14       studentJDBCTemplate.create("Ayan", 25, 100, 2011);
    15       System.out.println("------Listing all the records--------" );
    16       List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents();
    17       for (StudentMarks record : studentMarks) {
    18          System.out.print("ID : " + record.getId() );
    19          System.out.print(", Name : " + record.getName() );
    20          System.out.print(", Marks : " + record.getMarks());
    21          System.out.print(", Year : " + record.getYear());
    22          System.out.println(", Age : " + record.getAge());
    23       }
    24    }
    25 }

    以下是配置文件 Beans.xml 的内容:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     4     xmlns:context="http://www.springframework.org/schema/context"
     5     xmlns:tx="http://www.springframework.org/schema/tx"
     6     xmlns:aop="http://www.springframework.org/schema/aop"
     7     xsi:schemaLocation="http://www.springframework.org/schema/beans
     8    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     9    http://www.springframework.org/schema/context
    10    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    11    http://www.springframework.org/schema/tx
    12    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    13    http://www.springframework.org/schema/aop
    14    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    15 
    16     <!-- 开启注解 -->
    17     <context:annotation-config />
    18 
    19     <!-- 注解扫描包路径 -->
    20     <context:component-scan base-package="com.tutorialspoint.*" />
    21 
    22     <!-- 通过spring读取配置文件 -->
    23     <bean
    24         class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    25         <property name="fileEncoding" value="UTF-8"></property>
    26         <property name="locations">
    27             <list>
    28                 <value>classpath:conf_db.properties</value>
    29             </list>
    30         </property>
    31     </bean>
    32 
    33     <!-- Initialization for data source -->
    34     <bean id="dataSource"
    35         class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    36         <property name="driverClassName" value="${jdbc.url.driver}" />
    37         <property name="url" value="${jdbc.url.master}" />
    38         <property name="username" value="${jdbc.username.master}" />
    39         <property name="password" value="${jdbc.password.master}" />
    40     </bean>
    41 
    42     <!-- Initialization for TransactionManager -->
    43     <bean id="transactionManager"
    44         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    45         <property name="dataSource" ref="dataSource" />
    46     </bean>
    47     
    48     <!-- 支持事务注解 -->
    49     <tx:annotation-driven transaction-manager="transactionManager"/>
    50 
    51     <!-- Definition for studentJDBCTemplate bean -->
    52     <bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate">
    53         <property name="dataSource" ref="dataSource" />
    54     </bean>
    55 
    56 </beans>

    以下是数据库配置文件conf_db.properties的内容:

    jdbc.url.driver=com.mysql.jdbc.Driver
    jdbc.url.master=jdbc:mysql://localhost:3306/TEST
    jdbc.username.master=root
    jdbc.password.master=123456

    最后让我们在StudentJDBCTemplate类上面加上事务注解,此处不建议在Dao层使用事务注解,在平时工作中一般会使用在Service层,小Alan为了演示效果节约时间没有敲Service层代码,请注意,如图:

    当你完成了创建源和 bean 配置文件后,让我们运行应用程序。如果你的应用程序运行顺利的话,那么会输出如下所示的异常。在这种情况下,事务会回滚并且在数据库表中不会创建任何记录。

    ------Records creation--------
    Created Name = Zara, Age = 11
    Exception in thread "main" java.lang.RuntimeException: simulate Error condition

    在删除StudentJDBCTemplate类中create方法抛出异常的代码后,你可以尝试上述示例,在这种情况下,会提交事务并且你可以在数据库中看见记录。

    注意:项目jar包如图所示,其中也包含了Spring其它方面的一些jar包,小Alan偷一下懒就没去剔除了,这一块大家可以尝试只留下必要的jar包,以便熟悉Spring所包含的每个jar包在项目中所能够起到的作用,记得把jar包引入项目中才能够运行上述的示例。

    @Transactional注解

    @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

    虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

    默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

    @Transactional参数

    参数名称

    功能描述

    readOnly

    该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)

    rollbackFor

    该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:

    指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)

    指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

    rollbackForClassName

    该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:

    指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException")

    指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"})

    noRollbackFor

    该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:

    指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)

    指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

    noRollbackForClassName

    该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:

    指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException")

    指定多个异常类名称:

    @Transactional(noRollbackForClassName={"RuntimeException","Exception"})

    propagation

    该属性用于设置事务的传播行为。

    例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

    isolation

    该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

    timeout

    该属性用于设置事务的超时秒数,默认值为-1表示永不超时


    结束语:无论正在经历什么,都请不要轻言放弃,因为从来没有一种坚持会被辜负。谁的人生不是荆棘前行,生活从来不会一蹴而就,也不会永远安稳,只要努力,就能做独一无二平凡可贵的自己。

    可爱博主:AlanLee

    博客地址:http://www.cnblogs.com/AlanLee

    本文出自博客园,欢迎大家加入博客园。

  • 相关阅读:
    Jenkins安装(一)
    Ansible(一) 安装与简单测试
    zabbix中文乱码
    mysql5.7免密登录
    Zabbix 监控 Nginx 模板
    zabbix通过snmp监控主机
    zabbix5.0+grafana 使用脚本安装
    Eth-trunk配置-LACP模式
    Eth-Trunk配置-手动模式
    文件系统简单理解与实操(ext4)
  • 原文地址:https://www.cnblogs.com/AlanLee/p/8183645.html
Copyright © 2020-2023  润新知