热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spring(15):为业务层Service配置声明式事务(上)AOP配置

201811欢迎扫二维码关注公众号,获取技术干货业务层职能不仅仅是调用DAO这么简单,事务处理是EE开发不能回避的问题;业务层硬编码进行事

2018/1/1


欢迎扫二维码关注公众号,获取技术干货



      业务层职能不仅仅是调用DAO这么简单,事务处理是EE开发不能回避的问题;业务层硬编码进行事务管理弊端很大,Spring提供了声明式事务处理机制,它基于AOP实现,无须编写任何事务管理代码,所有工作全部在配置文件完成。

一、什么是事务?

答:

     理解事务管理之前,先通过一个例子讲一下什么是事务管理:取钱:比如你去ATM机取1000块钱,大体有两个步骤:(1)输入密码金额,银行卡扣掉1000元钱;(2)ATM出1000元钱。

     这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失1000元;如果银行卡扣钱失败但是ATM却出了1000块,那么银行将损失1000元。所以,如果一个步骤成功另一个步骤失败对双方都不是好事,如果不管哪一个步骤失败了以后,整个取钱过程都能回滚,也就是完全取消所有操作的话,这对双方都是极好的。 事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。 在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。

 

二、事务有哪些特质呢?

答:

·原子性(Atomicity):     事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
·一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完 成部分失败。在现实中的数据不应该被破坏。
·隔离性(Isolation):      可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
·持久性(Durability):     一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

 

三、Spring 框架的事务管理

     Spring 作为企业级应用程序框架,它在不同的事务管理API之上定义了一个抽象层。而应用程序开发人员不必了解底层的事务管理API,就可以使用Spring的事务管理机制。

     Spring既支持编程式事务管理,也支持声明式的事务管理!!

(1)编程式事务管理:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚,在编程式事务中,必须在每个业务操作中包含额外的事务管理代码

(2)声明式事务管理:大多数情况下比编程式事务管理更好用。它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。事务管理作为一种横切关注点,可以通过AOP方法模块化。Spring通过Spring AOP框架支持声明式事务管理。

 

四、Spring事务的传播属性

     当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。事务的传播行为可以由传播属性指定。

     Spring定义了7种传播行为:

 


传播行为含义
PROPAGATION_
MANDATORY
表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_
NESTED
表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务
PROPAGATION_
NEVER 
表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_
NOT_SUPPORTED
表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_REQUIRED表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_
REQUIRED_NEW
表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_SUPPORTS表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行

 

其中PROPAGATION_REQUIRED为默认的传播属性。

 

并发事务所导致的问题

在同一个应用程序或者不同应用程序中的多个事务在同一个数据集上并发执行时,可能会出现许多意外的问题。并发事务所导致的问题可以分为以下三类:

① 脏读:脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。

② 不可重复读:不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间更新了数据

③ 幻读:幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录

 

五、Spring 事务管理实例

 

下面是一个事务管理的小Demo:

【1】新建一个User.java 实体类:

 

package com.smbms.entities;import java.util.Date;
import java.util.List;public class User {private Integer id;private String userCode;private String userName;private String userPassword;private Integer gender;private Date birthday;private String phone;private String address ;private Integer userRole;private Integer createdBy;private Date creationDate;private Integer modifyBy;private Date modifyDate;private String userRoleName;private Role role;private List

addressList;public List
getAddressList() {return addressList;}public void setAddressList(List
addressList) {this.addressList = addressList;}public Role getRole() {return role;}public void setRole(Role role) {this.role = role;}public String getUserRoleName() {return userRoleName;}public void setUserRoleName(String userRoleName) {this.userRoleName = userRoleName;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUserCode() {return userCode;}public void setUserCode(String userCode) {this.userCode = userCode;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getUserPassword() {return userPassword;}public void setUserPassword(String userPassword) {this.userPassword = userPassword;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender = gender;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public Integer getUserRole() {return userRole;}public void setUserRole(Integer userRole) {this.userRole = userRole;}public Integer getCreatedBy() {return createdBy;}public void setCreatedBy(Integer createdBy) {this.createdBy = createdBy;}public Date getCreationDate() {return creationDate;}public void setCreationDate(Date creationDate) {this.creationDate = creationDate;}public Integer getModifyBy() {return modifyBy;}public void setModifyBy(Integer modifyBy) {this.modifyBy = modifyBy;}public Date getModifyDate() {return modifyDate;}public void setModifyDate(Date modifyDate) {this.modifyDate = modifyDate;}public User(Integer id, String userCode, String userName, String userPassword, Integer gender, Date birthday,String phone, String address, Integer userRole, Integer createdBy, Date creationDate, Integer modifyBy,Date modifyDate) {super();this.id = id;this.userCode = userCode;this.userName = userName;this.userPassword = userPassword;this.gender = gender;this.birthday = birthday;this.phone = phone;this.address = address;this.userRole = userRole;this.createdBy = createdBy;this.creationDate = creationDate;this.modifyBy = modifyBy;this.modifyDate = modifyDate;}public User() {super();// TODO 自动生成的构造函数存根}@Overridepublic String toString() {return "User [id=" + id + ", userCode=" + userCode + ", userName=" + userName + ", userPassword=" + userPassword+ ", gender=" + gender + ", birthday=" + birthday + ", phone=" + phone + ", address=" + address+ ", userRole=" + userRole + ", createdBy=" + createdBy + ", creationDate=" + creationDate+ ", modifyBy=" + modifyBy + ", modifyDate=" + modifyDate + "]";}}


【2】新建UserMaper.xml,添加以下内容:

 

 


insert into smbms_user (userCode,userName,userPassword,gender,birthday,phone,address,userRole,createBy , createDate)values (#{userCode},#{userName},#{userPassword},#{gender},#{birthday},#{phone},#{address},#{userRole},#{createdBy},#{creationDate})

 

 

 

 

 

 

【3】新建UserMapper.java接口,添加以下内容:

 

package com.smbms.dao;import java.util.List;
import java.util.Map;import org.apache.ibatis.annotations.Param;import com.smbms.entities.User;public interface UserMapper {/*** 下面定义的方法是用于--Spring声明式事务练习。* */public int add(User user);
}


【4】新建UserService.java接口和实现类UserServiceImpl.java:

 

UserService.java:

 

package com.smbms.service;import com.smbms.entities.User;public interface UserService {public boolean addNewUser(User user);
}


UserServiceImpl.java:

 

 

 

 

package com.smbms.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.smbms.dao.UserMapper;
import com.smbms.entities.User;@Service("userService")
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic boolean addNewUser(User user) {boolean result = false;try{if(userMapper.add(user) == 1){result = true; }}catch (RuntimeException e){e.printStackTrace();throw e;}return result;}public UserServiceImpl(UserMapper userMapper) {super();this.userMapper = userMapper;}public UserServiceImpl() {super();// TODO 自动生成的构造函数存根}public UserMapper getUserMapper() {return userMapper;}public void setUserMapper(UserMapper userMapper) {this.userMapper = userMapper;}}


【5】新建配置文件applicationContext-mybatis.xml,添加以下内容:

 

 





解释:

 

(1)为业务方法配置事务切面,需要用到tx 和 aop命名空间,首先需要导入它们;

(2)配置一个事务管理器组件,使用的是spring 的 DataSourceTransactionManager ,其配置方式如下:

 

 

    它提供了对事务处理的全面支持和统一管理,在切面中相当于增强处理的角色。记住要导入事先定义好的数据源组件DataSource。

(3)与之前增强处理不一样,事务管理器组件 还可以进一步配置,以便适应不同业务方法对于事务的不同要求。使用标签配置事务增强,设定事务的属性,为不同的业务方法指定具体的事务规则,其代码片段如下:

 

    而子标签,就是定制事务属性,通过标签进行设置。

 

(4)设置完了事务规则,最后还要定义切面,将事务规则应用在指定的方法上,代码片段如下:

 

 

(5)小结:加入组件,定义事务规则,AOP应用。

 

【6】写单元测试UserServiceImplTest.java:

 

package com.smbms.service;import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import com.smbms.entities.User;public class UserServiceImplTest {static Logger log = Logger.getLogger(UserServiceImplTest.class.getName());@Testpublic void test() {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-mybatis.xml");UserService userservice = (UserService) ctx.getBean("userService");User user = new User();user.setUserName("yuanshikai");boolean result = userservice.addNewUser(user);log.debug("testAdd result :"+result);}}


【7】工程说明:

 

 

(1)这是Mybatis和Spring整合练习的工程;
(2)实际使用有3个包:dao、entities、service;
(3)Mybatis配置文件内容很简洁,Spring完成大部分配置管理;
(4)通过SQLSessionTemplate的实现类对数据库进行操作;
(5)配置DAO组件并进入SqlSessionTemplate实例;
(6)配置业务Bean并注入DAO实例 ;(7)完成的功能是:查询provider的列表,模糊查询供应商信息;
(8)与smbms05MybatisSpring的区别:去掉了实现类ProviderMapperImpl;仅仅保留ProviderMapper
接口和相关SQL映射文件,通过MapperFactoryBean注入给业务组件。(9)此外,也新增了一个User查询,照壶画瓢;
(10)smbms06是使用MapperFactoryBean注入映射器,而smbms07是更加方便的使用MapperScannerConfigurer注入映射器。(11)smbms08增加新功能,实现按条件查询订单表Bill;使用resultMap做显示列表字段的自定义映射,使用Map传参,采用MapperFactoryBean注册映射器实现。[bill 和 provider 表]
(12) smbms09 在 smbms08 的基础上,进行拓展,用MapperScannerConfigurer注入映射器。
(13) smbms10,声明式事务,提高在XML配置文件注册org.springframework.jdbc.datasource.DataSourceTransactionManager,然后利用AOP切面对事务规则进行注入时。

 

 

 

综上,这里使用了xml对aop进行运用,从之前我们就知道,可以利用注解代替xml的配置,所以,不妨参考下一篇博文:《Spring(15):为业务层Service 配置声明式事务(下)-- 注解配置》

 


推荐阅读
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文介绍了多因子选股模型在实际中的构建步骤,包括风险源分析、因子筛选和体系构建,并进行了模拟实证回测。在风险源分析中,从宏观、行业、公司和特殊因素四个角度分析了影响资产价格的因素。具体包括宏观经济运行和宏经济政策对证券市场的影响,以及行业类型、行业生命周期和行业政策对股票价格的影响。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
author-avatar
手浪用户2602897055
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有