Java学习笔记(12)Spring JDBC框架和事务管理

Spring JDBC框架

JDBC框架概述:

在使用普通的 JDBC 数据库时,就会很麻烦的写不必要的代码来处理异常,打开和关闭数据库连接等。但 Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常,处理事务,到最后关闭连接。

所以当从数据库中获取数据时,你所做的是定义连接参数,指定要执行的 SQL 语句,每次迭代完成所需的工作。

Spring JDBC 提供几种方法和数据库中相应的不同的类与接口。我将给出使用 JdbcTemplate 类框架的经典和最受欢迎的方法。这是管理所有数据库通信和异常处理的中央框架类。

JdbcTemplate类型:

JdbcTemplate 类执行 SQL 查询、更新语句和存储过程调用,执行迭代结果集和提取返回参数值。它也捕获 JDBC 异常并转换它们到 org.springframework.dao 包中定义的通用类、更多的信息、异常层次结构。

JdbcTemplate 类的实例是线程安全配置的。所以你可以配置 JdbcTemplate 的单个实例,然后将这个共享的引用安全地注入到多个 DAOs 中。

使用 JdbcTemplate 类时常见的做法是在你的 Spring 配置文件中配置数据源,然后共享数据源 bean 依赖注入到 DAO 类中,并在数据源的设值函数中创建了 JdbcTemplate。

1
2
3
4
5
6
7
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/jsp_db"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>

数据访问对象(DAO):

DAO 代表常用的数据库交互的数据访问对象。DAOs 提供一种方法来读取数据并将数据写入到数据库中,它们应该通过一个接口显示此功能,应用程序的其余部分将访问它们。

在 Spring 中,数据访问对象(DAO)支持很容易用统一的方法使用数据访问技术,如 JDBC、Hibernate、JPA 或者 JDO。

Spring JDBC 示例:

目标:通过Spring JDBC 框架,来实现创建一个数据表。

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

StudentDAO.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package jdbc;

import java.util.List;
import javax.sql.DataSource;
public interface StudentDAO {

//用于初始化数据库连接的方法
public void setDataSource(DataSource dateSource);

//这是用来在学生表中创建一条记录的方法。
public void create(String name, Integer age);

//这是用于列出学生表中与通过的学生ID相对应的的记录的方法。
public Student getStudent(Integer id);

//这是用来列出学生表中的所有记录的方法。
public List<Student> listStudents();

//这是用于删除学生表中与通过的学生id相对应的记录的方法。
public void delete(Integer id);

//这是用来将一条记录更新到学生表中的方法。
public void update(Integer id, Integer age);
}

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package jdbc;

public class Student {
private Integer age;
private String name;
private Integer id;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
}

StudentMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class StudentMapper implements RowMapper<Student> {
public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
Student student = new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
return student;
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package jdbc;

import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

public class StudentJDBCTemplate implements StudentDAO{

private DataSource dataSource;
private JdbcTemplate jdbcTemplateObject;

@Override
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}

@Override
public void create(String name, Integer age) {
String SQL = "insert into Student (name, age) values (?, ?)";
jdbcTemplateObject.update(SQL, name, age);
System.out.println("创建记录名:" + name + "年龄:" + age);
return;
}

@Override
public Student getStudent(Integer id) {
String SQL = "select * from Student where id = ?";
Student student = jdbcTemplateObject.queryForObject(SQL,
new Object[]{id}, new StudentMapper());
return student;
}

@Override
public List<Student> listStudents() {
String SQL = "select * from Student";
List <Student> students = jdbcTemplateObject.query(SQL,
new StudentMapper());
return students;
}

@Override
public void delete(Integer id) {
String SQL = "delete from Student where id = ?";
jdbcTemplateObject.update(SQL, id);
System.out.println("删除ID为 : " + id + " 的记录!" );
return;
}

@Override
public void update(Integer id, Integer age) {
String SQL = "update Student set age = ? where id = ?";
jdbcTemplateObject.update(SQL, age, id);
System.out.println("更新记录的学生ID为 = " + id );
return;
}

}

MainApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package jdbc;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("JDBC.xml");
StudentJDBCTemplate studentJDBCTemplate =
(StudentJDBCTemplate)context.getBean("studentJDBCTemplate");
System.out.println("------创建记录--------" );
studentJDBCTemplate.create("xiaoming", 11);
studentJDBCTemplate.create("xiaoli", 2);
studentJDBCTemplate.create("xiaozhang", 15);
System.out.println();
System.out.println("------列出所有记录--------" );
List<Student> students = studentJDBCTemplate.listStudents();
for (Student record : students) {
System.out.print("ID : " + record.getId() );
System.out.print(", Name : " + record.getName() );
System.out.println(", Age : " + record.getAge());
}
System.out.println("----更新ID=2的记录 -----" );
studentJDBCTemplate.update(2, 20);
System.out.println("----列出ID=2的记录 -----" );
Student student = studentJDBCTemplate.getStudent(2);
System.out.print("ID : " + student.getId() );
System.out.print(", Name : " + student.getName() );
System.out.println(", Age : " + student.getAge());
}
}

配置文件 JDBC.xml 的内容 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">

<!-- Initialization for data source -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/jsp_db?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>

<!-- Definition for studentJDBCTemplate bean -->
<bean id="studentJDBCTemplate"
class="jdbc.StudentJDBCTemplate">
<property name="dataSource" ref="dataSource" />
</bean>

</beans>

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
------创建记录--------
创建记录名:xiaoming年龄:11
创建记录名:xiaoli年龄:2
创建记录名:xiaozhang年龄:15

------列出所有记录--------
ID : 1, Name : xiaoming, Age : 11
ID : 2, Name : xiaoli, Age : 20
ID : 3, Name : xiaozhang, Age : 15

----更新ID=2的记录 -----
更新记录的学生ID为 = 2

----列出ID=2的记录 -----
ID : 2, Name : xiaoli, Age : 20

Spring事务管理

事物管理:

一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID

  • 原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
  • 一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。
  • 隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
  • 持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。

一个真正的 RDBMS 数据库系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:

  • 使用 begin transaction 命令开始事务。
  • 使用 SQL 查询语句执行各种删除、更新或插入操作。
  • 如果所有的操作都成功,则执行提交操作,否则回滚所有操作。

Spring 框架在不同的底层事务管理 APIs 的顶部提供了一个抽象层。Spring 的事务支持旨在通过添加事务能力到 POJOs 来提供给 EJB 事务一个选择方案。Spring 支持编程式和声明式事务管理。EJBs 需要一个应用程序服务器,但 Spring 事务管理可以在不需要应用程序服务器的情况下实现。

局部事物和全局事物:

局部事务是特定于一个单一的事务资源,如一个 JDBC 连接,而全局事务可以跨多个事务资源事务,如在一个分布式系统中的事务。

局部事务管理在一个集中的计算环境中是有用的,该计算环境中应用程序组件和资源位于一个单位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局部事务更容易实现。

全局事务管理需要在分布式计算环境中,所有的资源都分布在多个系统中。在这种情况下事务管理需要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的执行需要全局事务管理系统和所有相关系统的局部数据管理人员之间的协调。

Spring支持两种类型的事物管理:

  • 编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
  • 声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。

声明式事务管理比编程式事务管理更可取,尽管它不如编程式事务管理灵活,但它允许你通过代码控制事务。但作为一种横切关注点,声明式事务管理可以使用 AOP 方法进行模块化。Spring 支持使用 Spring AOP 框架的声明式事务管理。

Spring事物抽象:

Spring 事务抽象的关键是由 org.springframework.transaction.PlatformTransactionManager 接口定义,如下所示:

1
2
3
4
5
6
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition);
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
序号 方法 & 描述
1 TransactionStatus getTransaction(TransactionDefinition definition)根据指定的传播行为,该方法返回当前活动事务或创建一个新的事务。
2 void commit(TransactionStatus status)该方法提交给定的事务和关于它的状态。
3 void rollback(TransactionStatus status)该方法执行一个给定事务的回滚。

TransactionDefinition 是在 Spring 中事务支持的核心接口,它的定义如下:

1
2
3
4
5
6
7
public interface TransactionDefinition {
int getPropagationBehavior();
int getIsolationLevel();
String getName();
int getTimeout();
boolean isReadOnly();
}
序号 方法 & 描述
1 int getPropagationBehavior()该方法返回传播行为。Spring 提供了与 EJB CMT 类似的所有的事务传播选项。
2 int getIsolationLevel()该方法返回该事务独立于其他事务的工作的程度。
3 String getName()该方法返回该事务的名称。
4 int getTimeout()该方法返回以秒为单位的时间间隔,事务必须在该时间间隔内完成。
5 boolean isReadOnly()该方法返回该事务是否是只读的。

下面是隔离级别的可能值:

序号 隔离 & 描述
1 TransactionDefinition.ISOLATION_DEFAULT这是默认的隔离级别。
2 TransactionDefinition.ISOLATION_READ_COMMITTED表明能够阻止误读;可以发生不可重复读和虚读。
3 TransactionDefinition.ISOLATION_READ_UNCOMMITTED表明可以发生误读、不可重复读和虚读。
4 TransactionDefinition.ISOLATION_REPEATABLE_READ表明能够阻止误读和不可重复读;可以发生虚读。
5 TransactionDefinition.ISOLATION_SERIALIZABLE表明能够阻止误读、不可重复读和虚读。

下面是传播类型的可能值:

序号 传播 & 描述
1 TransactionDefinition.PROPAGATION_MANDATORY支持当前事务;如果不存在当前事务,则抛出一个异常。
2 TransactionDefinition.PROPAGATION_NESTED如果存在当前事务,则在一个嵌套的事务中执行。
3 TransactionDefinition.PROPAGATION_NEVER不支持当前事务;如果存在当前事务,则抛出一个异常。
4 TransactionDefinition.PROPAGATION_NOT_SUPPORTED不支持当前事务;而总是执行非事务性。
5 TransactionDefinition.PROPAGATION_REQUIRED支持当前事务;如果不存在事务,则创建一个新的事务。
6 TransactionDefinition.PROPAGATION_REQUIRES_NEW创建一个新事务,如果存在一个事务,则把当前事务挂起。
7 TransactionDefinition.PROPAGATION_SUPPORTS支持当前事务;如果不存在,则执行非事务性。
8 TransactionDefinition.TIMEOUT_DEFAULT使用默认超时的底层事务系统,或者如果不支持超时则没有。

TransactionStatus 接口为事务代码提供了一个简单的方法来控制事务的执行和查询事务状态。

1
2
3
4
5
6
7
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
boolean isCompleted();
}
序号 方法 & 描述
1 boolean hasSavepoint()该方法返回该事务内部是否有一个保存点,也就是说,基于一个保存点已经创建了嵌套事务。
2 boolean isCompleted()该方法返回该事务是否完成,也就是说,它是否已经提交或回滚。
3 boolean isNewTransaction()在当前事务时新的情况下,该方法返回 true。
4 boolean isRollbackOnly()该方法返回该事务是否已标记为 rollback-only。
5 void setRollbackOnly()该方法设置该事务为 rollback-only 标记。

Spring编程式事物管理:

编程式事务管理方法允许你在对你的源代码编程的帮助下管理事务。这给了你极大地灵活性,但是它很难维护。

首先得创建两张表:

Student表:

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

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

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

直接使用 PlatformTransactionManager 来实现编程式方法从而实现事务。要开始一个新事务,你需要有一个带有适当的 transaction 属性的 TransactionDefinition 的实例。这个例子中,我们使用默认的 transaction 属性简单的创建了 DefaultTransactionDefinition 的一个实例。

当 TransactionDefinition 创建后,你可以通过调用 getTransaction() 方法来开始你的事务,该方法会返回 TransactionStatus 的一个实例。 TransactionStatus 对象帮助追踪当前的事务状态,并且最终,如果一切运行顺利,你可以使用 PlatformTransactionManagercommit() 方法来提交这个事务,否则的话,你可以使用 rollback() 方法来回滚整个操作。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package transaction;

import java.util.List;

import javax.sql.DataSource;

public interface StudentDAO {
//这是用于初始化数据库资源(即连接)的方法。
public void setDataSource(DataSource ds);

//这是用来在学生和标记表中创建一条记录的方法。
public void create(String name, Integer age, Integer marks, Integer year);

//这是用来列出学生和标记表中的所有记录的方法
public List<StudentMarks> listStudents();
}

StudentMarks.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package transaction;

public class StudentMarks {
private Integer age;
private String name;
private Integer id;
private Integer marks;
private Integer year;
private Integer sid;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getMarks() {
return marks;
}
public void setMarks(Integer marks) {
this.marks = marks;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}

}

StudentMarksMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package transaction;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class StudentMarksMapper implements RowMapper<StudentMarks> {

@Override
public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {
StudentMarks studentMarks = new StudentMarks();
studentMarks.setId(rs.getInt("id"));
studentMarks.setAge(rs.getInt("age"));
studentMarks.setName(rs.getString("name"));
studentMarks.setMarks(rs.getInt("marks"));
studentMarks.setYear(rs.getInt("year"));
studentMarks.setSid(rs.getInt("sid"));
return studentMarks;
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package transaction;

import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class StudentJDBCTemplate implements StudentDAO {
private DataSource dataSource;
private JdbcTemplate jdbcTemplateObject;
private PlatformTransactionManager transactionManager;
@Override
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}

public void setTransactionManager(
PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public void create(String name, Integer age, Integer marks, Integer year){
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
String SQL1 = "insert into Student (name, age) values (?, ?)";
jdbcTemplateObject.update( SQL1, name, age);
// 获取要在标记表中使用的最新学生ID.
String SQL2 = "select max(id) from Student";
int sid = jdbcTemplateObject.queryForObject(SQL2, Integer.class);
String SQL3 = "insert into Marks(sid, marks, year) " +
"values (?, ?, ?)";
jdbcTemplateObject.update( SQL3, sid, marks, year);
System.out.println("Created Name = " + name + ", Age = " + age);
transactionManager.commit(status);
} catch (DataAccessException e) {
System.out.println("Error in creating record, rolling back");
transactionManager.rollback(status);
throw e;
}
return;
}
@Override
public List<StudentMarks> listStudents() {
String SQL = "select * from Student, Marks where Student.id=Marks.sid";
List <StudentMarks> studentMarks = jdbcTemplateObject.query(SQL,
new StudentMarksMapper());
return studentMarks;
}
}

MainApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package transaction;

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import transaction.StudentJDBCTemplate;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("transaction.xml");
StudentJDBCTemplate studentJDBCTemplate =
(StudentJDBCTemplate)context.getBean("studentJDBCTemplate");
System.out.println("------创建记录--------" );
studentJDBCTemplate.create("Zara", 11, 99, 2010);
studentJDBCTemplate.create("Nuha", 20, 97, 2010);
studentJDBCTemplate.create("Ayan", 25, 100, 2011);
System.out.println("------列出所有记录--------" );
List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents();
for (StudentMarks record : studentMarks) {
System.out.print("ID : " + record.getId() );
System.out.print(", Name : " + record.getName() );
System.out.print(", Marks : " + record.getMarks());
System.out.print(", Year : " + record.getYear());
System.out.println(", Age : " + record.getAge());
}
}
}

配置文件:transacation.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">

<!-- Initialization for data source -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/jsp_db?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>

<!-- Initialization for TransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<!-- Definition for studentJDBCTemplate bean -->
<bean id="studentJDBCTemplate"
class="transaction.StudentJDBCTemplate">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
</bean>

</beans>

程序运行结果:

1
2
3
4
5
6
7
8
------创建记录--------
Created Name = Zara, Age = 11
Created Name = Nuha, Age = 20
Created Name = Ayan, Age = 25
------列出所有记录--------
ID : 1, Name : Zara, Marks : 99, Year : 2010, Age : 11
ID : 2, Name : Nuha, Marks : 97, Year : 2010, Age : 20
ID : 3, Name : Ayan, Marks : 100, Year : 2011, Age : 25

Spring声明式事务管理:

声明式事务管理方法允许你在配置的帮助下而不是源代码硬编程来管理事务。这意味着你可以将事务管理从事务代码中隔离出来。你可以只使用注释或基于配置的 XML 来管理事务。 bean 配置会指定事务型方法。下面是与声明式事务相关的步骤:

  • 我们使用标签,它创建一个事务处理的建议,同时,我们定义一个匹配所有方法的切入点,我们希望这些方法是事务型的并且会引用事务型的建议。
  • 如果在事务型配置中包含了一个方法的名称,那么创建的建议在调用方法之前就会在事务中开始进行。
  • 目标方法会在 try / catch 块中执行。
  • 如果方法正常结束,AOP 建议会成功的提交事务,否则它执行回滚操作。

StudentDAO.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package transaction;

import java.util.List;

import javax.sql.DataSource;

public interface StudentDAO {
//这是用于初始化数据库资源(即连接)的方法。
public void setDataSource(DataSource ds);

//这是用来在学生和标记表中创建一条记录的方法。
public void create(String name, Integer age, Integer marks, Integer year);

//这是用来列出学生和标记表中的所有记录的方法
public List<StudentMarks> listStudents();
}

StudentMarks.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package transaction;

public class StudentMarks {
private Integer age;
private String name;
private Integer id;
private Integer marks;
private Integer year;
private Integer sid;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getMarks() {
return marks;
}
public void setMarks(Integer marks) {
this.marks = marks;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
}

StudentMarksMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package transaction;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class StudentMarksMapper implements RowMapper<StudentMarks> {

@Override
public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {
StudentMarks studentMarks = new StudentMarks();
studentMarks.setId(rs.getInt("id"));
studentMarks.setAge(rs.getInt("age"));
studentMarks.setName(rs.getString("name"));
studentMarks.setMarks(rs.getInt("marks"));
studentMarks.setYear(rs.getInt("year"));
studentMarks.setSid(rs.getInt("sid"));
return studentMarks;
}
}

StudentJDBCTemplate.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package transaction;

import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class StudentJDBCTemplate implements StudentDAO {
private DataSource dataSource;
private JdbcTemplate jdbcTemplateObject;
private PlatformTransactionManager transactionManager;
@Override
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}

public void setTransactionManager(
PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public void create(String name, Integer age, Integer marks, Integer year){
try {
String SQL1 = "insert into Student (name, age) values (?, ?)";
jdbcTemplateObject.update( SQL1, name, age);
//获取要在标记表中使用的最新学生ID。
String SQL2 = "select max(id) from Student";
int sid = jdbcTemplateObject.queryForObject(SQL2, Integer.class );
String SQL3 = "insert into Marks(sid, marks, year) " +
"values (?, ?, ?)";
jdbcTemplateObject.update( SQL3, sid, marks, year);
System.out.println("Created Name = " + name + ", Age = " + age);
// 来模拟异常
throw new RuntimeException("simulate Error condition") ;
} catch (DataAccessException e) {
System.out.println("创建记录时的错误, 回滚");
throw e;
}
}
@Override
public List<StudentMarks> listStudents() {
String SQL = "select * from Student, Marks where Student.id=Marks.sid";
List <StudentMarks> studentMarks = jdbcTemplateObject.query(SQL,
new StudentMarksMapper());
return studentMarks;
}
}

MainApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package transaction;

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("transaction2.xml");
StudentDAO studentJDBCTemplate =
(StudentDAO)context.getBean("studentJDBCTemplate");
System.out.println("------创建记录--------" );
studentJDBCTemplate.create("Zara", 11, 99, 2010);
studentJDBCTemplate.create("Nuha", 20, 97, 2010);
studentJDBCTemplate.create("Ayan", 25, 100, 2011);
System.out.println("------列出所有记录--------" );
List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents();
for (StudentMarks record : studentMarks) {
System.out.print("ID : " + record.getId() );
System.out.print(", Name : " + record.getName() );
System.out.print(", Marks : " + record.getMarks());
System.out.print(", Year : " + record.getYear());
System.out.println(", Age : " + record.getAge());
}
}
}

配置文件transcation2.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

<!-- Initialization for data source -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/jsp_db?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="create"/>
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="createOperation"
expression="execution(* transaction2.StudentJDBCTemplate.create(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="createOperation"/>
</aop:config>

<!-- Initialization for TransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<!-- Definition for studentJDBCTemplate bean -->
<bean id="studentJDBCTemplate"
class="transaction2.StudentJDBCTemplate">
<property name="dataSource" ref="dataSource" />
</bean>

</beans>

运行结果:

1
2
3
------创建记录--------
Created Name = Zara, Age = 11
Exception in thread "main" java.lang.RuntimeException: simulate Error condition
坚持原创技术分享,您的支持将鼓励我继续创作!