Spring事务管理示例JDBC

Spring Transaction Management是Spring框架中使用最广泛且最重要的特性之一。事务管理在任何企业应用程序中都是一项微不足道的任务。我们已经学习了如何将JDBC API用于事务管理。Spring为事务管理提供了广泛的支持,并帮助开发人员更多地关注业务逻辑,而不是担心任何系统故障导致的数据完整性。

目录[ 隐藏 ]

  • 1 Spring事务管理
  • 2 Spring事务管理JDBC示例
    • 2.1 Spring事务管理 - 数据库设置
    • 2.2 Spring事务管理 - Maven依赖关系
    • 2.3 Spring事务管理 - 模型类
    • 2.4 Spring事务管理 - DAO实现
    • 2.5 Spring声明式事务管理 - 服务
    • 2.6 Spring事务管理 - Bean配置

Spring事务管理

使用Spring Transaction Management的一些好处是:

  1. 支持声明式事务管理。在此模型中,Spring在事务方法上使用AOP来提供数据完整性。这是首选方法,适用于大多数情况。
  2. 支持大多数事务API,如JDBC,Hibernate,JPA,JDO,JTA等。我们需要做的就是使用正确的事务管理器实现类。例如,org.springframework.jdbc.datasource.DriverManagerDataSource对于JDBC事务管理,org.springframework.orm.hibernate3.HibernateTransactionManager如果我们使用Hibernate作为ORM工具。
  3. 通过使用TransactionTemplate PlatformTransactionManager实施支持程序化事务管理。

我们在事务管理器中需要的大多数功能都由Declarative事务管理支持,因此我们将这种方法用于示例项目。

Spring事务管理JDBC示例

我们将创建一个简单的Spring JDBC项目,我们将在单个事务中更新多个表。事务应该仅在所有JDBC语句成功执行时提交,否则应该回滚以避免数据不一致。

如果您了解JDBC事务管理,您可能会认为我们可以通过为连接设置auto-commit为false并根据所有语句的结果(提交或回滚事务)轻松完成。显然我们可以做到,但这将导致很多锅炉板代码仅用于交易管理。此外,相同的代码将出现在我们正在寻找事务管理的所有位置,从而导致紧密耦合和不可维护的代码。

Spring声明式事务管理通过使用面向方面编程来实现松散耦合并避免在我们的应用程序中使用样板代码来解决这些问题。让我们看一下Spring如何通过一个简单的例子来做到这一点。

在我们进入Spring项目之前,让我们为我们的使用做一些数据库设置。

Spring Transaction Management - 数据库设置

我们将创建两个表供我们使用,并在单个事务中更新它们。


CREATE TABLE `Customer` (`id` int(11) unsigned NOT NULL,`name` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `Address` (`id` int(11) unsigned NOT NULL,`address` varchar(20) DEFAULT NULL,`country` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我们可以在这里定义外键关系,从Address id列到Customer id列,但为了简单起见,我没有在这里定义任何约束。

我们的数据库设置已准备好进行spring事务管理项目,让我们在Spring Tool Suite中创建一个简单的Spring Maven项目。我们的最终项目结构如下图所示。

让我们逐个研究每个部分,它们将一起提供一个简单的Spring事务管理示例和JDBC。

Spring事务管理 - Maven依赖

由于我们使用的是JDBC API,因此我们必须在应用程序中包含spring-jdbc依赖项。我们还需要MySQL数据库驱动程序连接到mysql数据库,所以我们也将包含mysql-connector-java依赖。

spring-tx artifact提供事务管理依赖,通常它由STS自动包含,但如果不是,那么你也需要包含它。您可能会看到一些其他依赖项用于日志记录和单元测试,但我们不会使用其中任何一个。我们的最终pom.xml文件如下所示。


<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>org.springframework.samples</groupId><artifactId>SpringJDBCTransactionManagement</artifactId><version>0.0.1-SNAPSHOT</version><properties><!-- Generic properties --><java.version>1.7</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><!-- Spring --><spring-framework.version>4.0.2.RELEASE</spring-framework.version><!-- Logging --><logback.version>1.0.13</logback.version><slf4j.version>1.7.5</slf4j.version><!-- Test --><junit.version>4.11</junit.version></properties><dependencies><!-- Spring and Transactions --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring-framework.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring-framework.version}</version></dependency><!-- Spring JDBC and MySQL Driver --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring-framework.version}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.0.5</version></dependency><!-- Logging with SLF4J & LogBack --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version><scope>compile</scope></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version><scope>runtime</scope></dependency><!-- Test Artifacts --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring-framework.version}</version><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>test</scope></dependency></dependencies>
</project>

我已将Spring版本更新为今天的最新版本。确保MySQL数据库驱动程序与您的mysql安装兼容。

Spring事务管理 - 模型类

我们将创建两个Java Bean,Customer和Address,它们将映射到我们的表。


package com.journaldev.spring.jdbc.model;public class Address {private int id;private String address;private String country;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getCountry() {return country;}public void setCountry(String country) {this.country = country;}}

package com.journaldev.spring.jdbc.model;public class Customer {private int id;private String name;private Address address;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}}

请注意,Customer bean将Address作为其中一个变量。当我们为客户实现DAO时,我们将获得客户和地址表的数据,我们将为这些表执行两个单独的插入查询,这就是我们需要事务管理以避免数据不一致的原因。

Spring事务管理 - DAO实现

让我们为Customer bean实现DAO,为简单起见,我们将只有一种方法在客户和地址表中插入记录。


package com.journaldev.spring.jdbc.dao;import com.journaldev.spring.jdbc.model.Customer;public interface CustomerDAO {public void create(Customer customer);
}

package com.journaldev.spring.jdbc.dao;import javax.sql.DataSource;import org.springframework.jdbc.core.JdbcTemplate;import com.journaldev.spring.jdbc.model.Customer;public class CustomerDAOImpl implements CustomerDAO {private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}@Overridepublic void create(Customer customer) {String queryCustomer = "insert into Customer (id, name) values (?,?)";String queryAddress = "insert into Address (id, address,country) values (?,?,?)";JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);jdbcTemplate.update(queryCustomer, new Object[] { customer.getId(),customer.getName() });System.out.println("Inserted into Customer Table Successfully");jdbcTemplate.update(queryAddress, new Object[] { customer.getId(),customer.getAddress().getAddress(),customer.getAddress().getCountry() });System.out.println("Inserted into Address Table Successfully");}}

请注意,CustomerDAO实施不会处理事务管理。这样我们就可以实现关注点的分离,因为有时我们会从第三方获得DAO实现,而我们无法控制这些类。

Spring声明式事务管理 - 服务

让我们创建一个客户服务,它将使用CustomerDAO实现,并在单个方法中在客户和地址表中插入记录时提供事务管理。


package com.journaldev.spring.jdbc.service;import com.journaldev.spring.jdbc.model.Customer;public interface CustomerManager {public void createCustomer(Customer cust);
}

package com.journaldev.spring.jdbc.service;import org.springframework.transaction.annotation.Transactional;import com.journaldev.spring.jdbc.dao.CustomerDAO;
import com.journaldev.spring.jdbc.model.Customer;public class CustomerManagerImpl implements CustomerManager {private CustomerDAO customerDAO;public void setCustomerDAO(CustomerDAO customerDAO) {this.customerDAO = customerDAO;}@Override@Transactionalpublic void createCustomer(Customer cust) {customerDAO.create(cust);}}

如果您注意到CustomerManager实现,它只是使用CustomerDAO实现来创建客户,但通过使用注释注释createCustomer()方法来提供声明式事务管理@Transactional。这就是我们在代码中需要做的所有事情,以获得Spring事务管理的好处。

@Transactional注释可以应用于方法和整个类。如果您希望所有方法都具有事务管理功能,则应使用此批注对类进行批注。阅读Java Annotations Tutorial中有关注释的更多信息。
剩下的唯一部分是布线弹簧bean,以使弹簧事务管理示例工作。

Spring事务管理 - Bean配置

创建一个名为“spring.xml”的Spring Bean配置文件。我们将在测试程序中使用它来连接spring bean并执行我们的JDBC程序来测试事务管理。


<?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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"><!-- Enable Annotation based Declarative Transaction Management --><tx:annotation-driven proxy-target-class="true"transaction-manager="transactionManager" /><!-- Creating TransactionManager Bean, since JDBC we are creating of type DataSourceTransactionManager --><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!-- MySQL DB DataSource --><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/TestDB" /><property name="username" value="pankaj" /><property name="password" value="pankaj123" /></bean><bean id="customerDAO" class="com.journaldev.spring.jdbc.dao.CustomerDAOImpl"><property name="dataSource" ref="dataSource"></property></bean><bean id="customerManager" class="com.journaldev.spring.jdbc.service.CustomerManagerImpl"><property name="customerDAO" ref="customerDAO"></property></bean></beans>

spring bean配置文件中需要注意的重点是:

  • tx:annotation-driven元素用于告诉Spring上下文我们正在使用基于注释的事务管理配置。transaction-manager属性用于提供事务管理器bean名称。事务管理器默认值是transactionManager,但我仍然要避免混淆。proxy-target-class属性用于告诉Spring上下文使用基于类的代理,如果没有它,您将获得运行时异常,例如线程“main”中的异常org.springframework.beans.factory.BeanNotOfRequiredTypeException:名为'customerManager'的Bean必须是[com.journaldev.spring.jdbc.service.CustomerManagerImpl]类型,但实际上是[com.sun.proxy。$ Proxy6]类型
  • 由于我们使用JDBC,因此我们创建了类型的transactionManager bean org.springframework.jdbc.datasource.DataSourceTransactionManager。这非常重要,我们应该根据我们的事务API使用正确的事务管理器实现类。
  • dataSource bean用于创建DataSource对象,我们需要提供数据库配置属性,如driverClassName,url,username和password。根据您的本地设置更改这些值。
  • 我们正在将dataSource注入到customerDAO bean中。类似地,我们将customerDAO bean注入customerManager bean定义。

我们的设置准备就绪,让我们创建一个简单的测试类来测试我们的事务管理实现。


package com.journaldev.spring.jdbc.main;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.journaldev.spring.jdbc.model.Address;
import com.journaldev.spring.jdbc.model.Customer;
import com.journaldev.spring.jdbc.service.CustomerManager;
import com.journaldev.spring.jdbc.service.CustomerManagerImpl;public class TransactionManagerMain {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");CustomerManager customerManager = ctx.getBean("customerManager",CustomerManagerImpl.class);Customer cust = createDummyCustomer();customerManager.createCustomer(cust);ctx.close();}private static Customer createDummyCustomer() {Customer customer = new Customer();customer.setId(2);customer.setName("Pankaj");Address address = new Address();address.setId(2);address.setCountry("India");// setting value more than 20 chars, so that SQLException occursaddress.setAddress("Albany Dr, San Jose, CA 95129");customer.setAddress(address);return customer;}}

请注意,我显式设置地址列值太长,以便在将数据插入Address表时会出现异常。

现在,当我们运行测试程序时,我们得到以下输出。


Mar 29, 2014 7:59:32 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: startup date [Sat Mar 29 19:59:32 PDT 2014]; root of context hierarchy
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Inserted into Customer Table Successfully
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.support.SQLErrorCodesFactory <init>
INFO: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
Exception in thread "main" org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into Address (id, address,country) values (?,?,?)]; Data truncation: Data too long for column 'address' at row 1; nested exception is com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'address' at row 1at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:100)at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:658)at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:907)at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:968)at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:978)at com.journaldev.spring.jdbc.dao.CustomerDAOImpl.create(CustomerDAOImpl.java:27)at com.journaldev.spring.jdbc.service.CustomerManagerImpl.createCustomer(CustomerManagerImpl.java:19)at com.journaldev.spring.jdbc.service.CustomerManagerImpl$$FastClassBySpringCGLIB$$84f71441.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)at com.journaldev.spring.jdbc.service.CustomerManagerImpl$$EnhancerBySpringCGLIB$$891ec7ac.createCustomer(<generated>)at com.journaldev.spring.jdbc.main.TransactionManagerMain.main(TransactionManagerMain.java:20)
Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'address' at row 1at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2939)at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1623)at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1715)at com.mysql.jdbc.Connection.execSQL(Connection.java:3249)at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1268)at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1541)at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1455)at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1440)at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:914)at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:907)at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:642)... 16 more

请注意,日志消息表明插入到customer表中的数据成功但MySQL数据库驱动程序抛出的异常清楚地表明该值对于地址列来说太长了。现在,如果您将检查Customer表,那么您将找不到任何行,这意味着事务将完全回滚。

如果您想知道事务管理魔术在哪里发生,请仔细查看日志并注意Spring框架创建的AOP和Proxy类。Spring框架使用Around建议为CustomerManagerImpl生成代理类,并且只有在方法成功返回时才提交事务。如果有任何异常,它只是回滚整个事务。我建议你阅读Spring AOP示例,以了解有关面向方面编程模型的更多信息。

这就是Spring Transaction Management示例,从下面的链接下载示例项目并使用它来了解更多信息。

下载Spring JDBC事务管理项目

转载来源:https://www.journaldev.com/2603/spring-transaction-management-jdbc-example

Spring事务管理示例JDBC相关推荐

  1. spring事务 jdbc_Spring事务管理示例JDBC

    spring事务 jdbc Spring Transaction Management is one of the most widely used and important feature of ...

  2. Spring jdbc Template和Spring 事务管理

    使用jdbcTemplate完成增删改查操作(重点) package com.it.jdbctemplate;import java.util.List;import org.junit.Test; ...

  3. Spring事务管理 与 SpringAOP

    1,Spring事务的核心接口 Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略.  ...

  4. Spring事务管理(详解+实例)

    写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: ...

  5. Spring事务管理全面分析

    Spring 事务属性分析 什么是事物   事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能 ...

  6. Spring 事务管理

    http://www.redsaga.com/spring_ref/2.0/html/transaction.html#transaction-declarative 9.1. 简介 Spring框架 ...

  7. Spring事务管理详解

    什么是事务 事务是逻辑上的一组操作,要么都执行,要么都不执行. 需要注意的是:事务能否生效数据库引擎是否支持事务是关键.比如常用的 MySQL 数据库默认使用支持事务的 innodb引擎.但是,如果把 ...

  8. Spring DAO(3):Spring 事务管理

    Spring 对于事务管理的支持 关于事务管理的基本概念,查看 http://blog.csdn.net/al_assad/article/details/78808820 JDBC 底层已经提供了对 ...

  9. Spring 事务管理及失效总结

    Spring 事务管理及失效总结 所谓事务管理,其实就是"按照给定的事务规则来执行提交或者回滚操作". Spring 并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职 ...

最新文章

  1. Python检查系统可疑用户
  2. mysql 网络io_分布式 | DBLE 网络模块源码解析(一):网络 IO 基础知识
  3. 遇到问题为何该自己动手
  4. 差动机器人毕业设计_双轮差动机器人曲线算法设计与实现
  5. db2分页查询语句优化_数据量很大,分页查询很慢,该怎么优化?
  6. SPOJ7258(后缀自动机--第k大的子串)
  7. 大数据架构如何做到流批一体?【对于Flink等流批一体的概念做了很好的澄清!】
  8. 在mac上安装Go语言初体验
  9. php 什么函数获取ip,在PHP中获取ip地址的方法有哪些
  10. 访谈编码怎么做_怎么才能让口才得到提升
  11. vue实现搜索框记录搜索历史_云开发版的微信商城小程序第四章,首页自定义搜索框的实现...
  12. UDP网络编程-广播
  13. 台式计算机文件打不开怎么回事,电脑文件打不开怎么回事
  14. 价值 1500 美元的 iPhone 值得买吗
  15. 【CodeForces 767C】Garland (树形DP)
  16. 概率论与数理统计公式
  17. mbk文件导入到oracle,将Oracle DMP文件导入到oracle的全新安装中
  18. 程序员之禅的10条黄金法则
  19. 如何用大数据进行咖啡店选址要素分析
  20. STM32L4R9ZIY6PTR STM32L4高性能嵌入式—MCU

热门文章

  1. 什么是MARC数据?
  2. JS判断浏览器是否支持某一个CSS3属性
  3. Moodle插件开发笔记
  4. 洛谷OJ上的A+B花(zhuang)式(bi)解法
  5. 真的没办法一心一意麽? php 文件操作
  6. book1复习 使用java理解程序逻辑
  7. ListView控件获取选中项的内容 c# 114867417
  8. 案例 解析频道节目单 c# 1614261055
  9. 寒假作业 使用xmind脑图小结课程内容
  10. Timer定时器控件 1130