公司的项目已经接近尾声了,总结一下项目中用到的技术,我发现项目中的有些东西还是挺模糊的,只是知道这么用就行了。并不清楚其中的原理。由于公司的项目比较老,是7年前的一个项目了,中间一直有人在维护,也是在这个过程中不断融入了新的东西,比如就项目的持久化这块来说,就用了ibatis、mybatis、hibernate、spring JDBC四种混合的框架。究其原因只能说是历史遗留问题,就不做过多的解释了。但是这么多持久化的框架如何协同工作的,尤其是事务的控制,一个系统中使用如此多的持久化框架是,他们是如何正确的进行事务控制的,甚至有些业务逻辑同时调用了使用这些不同框架的方法。这听起来确实有些困难。我们知道Spring为每种数据访问技术提供了相应的事务管理器,难道需要分别为它们配置对应的事务管理器吗?它们到底是如何协作和工作的呢?这些层出不穷的问题往往压制了开发人员使用联合军种的想法。
Spring事务管理器的应对
Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋。像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不太方便;而iBatis则通过模板化技术让你方便地控制SQL,但没有Hibernate那样高的开发效率;*度最高的当然是直接使用SpringJDBC了,但它也是底层的,灵活的代价是代码的繁复。很难说哪种数据访问技术是最优秀的,只有在某种特定的场景下才能给出答案。所以在一个应用中,往往采用多个数据访问技术:一般是两种,一种采用ORM技术框架,而另一种采用偏JDBC的底层技术,两者珠联璧合,形成联合军种,共同御敌。
其实,在这个问题上,我们低估了Spring事务管理的能力。
如果你采用了一个高端ORM技术(Hibernate、JPA、JDO),同时采用一个JDBC技术(Spring JDBC、iBatis),
由于前者的会话(Session)是对后者连接(Connection)的封装,Spring会“足够智能地”在同一个事务线程让前者的会话封装后者的连接。
所以,我们只要直接采用前者的事务管理器就可以了。
表1给出了混合数据访问技术框架所对应的事务管理器。
序 号 |
混合数据访问技术框架 |
事务管理器 |
1 |
Hibernate+ Spring JDBC或iBatis |
org.springframework.orm.hibernate3.HibernateTransactionManager |
2 |
JPA+Spring JDBC或iBatis |
org.springframework.orm.jpa.JpaTransactionManager |
3 |
JDO+Spring JDBC或iBatis |
org.springframework.orm.jdo.JdoTransactionManager |
Hibernate+Spring JDBC混合框架的事务管理
由于一般不会出现同时使用多个ORM框架的情况(如Hibernate+JPA),我们不拟对此命题展开论述,只重点研究ORM框架+JDBC框架的情况。Hibernate+Spring JDBC可能是被使用得最多的组合,本节我们通过实例观察事务管理的运作情况。
package com.baobaotao.mixdao; … @Service("userService")
public class UserService extends BaseService {
@Autowired
private HibernateTemplate hibernateTemplate; @Autowired
private ScoreService scoreService; public void logon(String userName) { //①通过Hibernate技术访问数据
System.out.println("before updateLastLogonTime()..");
updateLastLogonTime(userName);
System.out.println("end updateLastLogonTime().."); //②通过JDBC技术访问数据
System.out.println("before scoreService.addScore()..");
scoreService.addScore(userName, 20);
System.out.println("end scoreService.addScore()..");
} public void updateLastLogonTime(String userName) {
User user = hibernateTemplate.get(User.class,userName);
user.setLastLogonTime(System.currentTimeMillis());
hibernateTemplate.update(user); //③这句很重要,请看下文的分析
hibernateTemplate.flush();
}
}
在①处,使用Hibernate操作数据,而在②处调用ScoreService#addScore(),该方法内部使用Spring JDBC操作数据。
在③处,我们显式调用了flush()方法,将Session中的缓存同步到数据库中(即马上向数据库发送一条更新记录的SQL语句)。之所以要显式执行flush()方法,原因是在默认情况下,Hibernate对数据的更改只是记录在一级缓存中,要等到事务提交或显式调用flush()方法时才会将一级缓存中的数据同步到数据库中,而提交事务的操作发生在 logon()方法返回前。如果所有针对数据库的更改操作都使用Hibernate,这种数据同步的延迟机制并不会产生任何问题。但是,我们在logon()方法中同时采用了Hibernate和Spring JDBC混合数据访问技术,Spring JDBC无法自动感知Hibernate一级缓存,所以如果不及时调用flush()方法将记录数据更改的一级缓存同步到数据库中,则②处通过Spring JDBC进行数据更改的结果将被Hibernate一级缓存中的更改覆盖掉,因为Hibernate一级缓存要等到logon()方法返回前才同步到数据库!
ScoreService使用SpringJDBC数据访问技术,其代码如下所示:
package com.baobaotao.mixdao; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.apache.commons.dbcp.BasicDataSource; @Service("scoreService")
public class ScoreService extends BaseService{ @Autowired
private JdbcTemplate jdbcTemplate; public void addScore(String userName, int toAdd) {
String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";
jdbcTemplate.update(sql, toAdd, userName);
BasicDataSource basicDataSource = (BasicDataSource) jdbcTemplate.getDataSource();
//①查看此处数据库激活的连接数量
System.out.println("[scoreUserService.addScore]激活连接数量:"
+basicDataSource.getNumActive());
}
}
Spring关键的配置文件代码如下所示:
启动Spring容器,执行UserService#logon()方法,可以查看到如下的执行日志:
仔细观察这段输出日志,在①处UserService#logon()开启一个新的事务。②处的UserService#updateLastLogonTime() 绑定到事务上下文的Session中。③处ScoreService#addScore()方法加入到①处开启的事务上下文中。④处的输出是ScoreService#addScore()方法内部的输出信息,汇报此时数据源激活的连接数为1,这清楚地告诉我们Hibernate和JDBC这两种数据访问技术在同一事务上下文中“共用”一个连接。在⑤处,提交Hibernate事务,接着在⑥处触发调用底层的Connection提交事务。
从以上的运行结果,我们可以得出这样的结论:使用Hibernate事务管理器后,可以混合使用Hibernate和SpringJDBC数据访问技术,它们将工作于同一事务上下文中。但是使用SpringJDBC访问数据时,Hibernate的一级或二级缓存得不到同步,此外,一级缓存延迟数据同步机制可能会覆盖SpringJDBC数据更改的结果。
由于混合数据访问技术方案存在“事务同步而缓存不同步”的情况,所以最好用Hibernate进行读写操作,而只用SpringJDBC进行读操作。如用Spring JDBC进行简要列表的查询,而用Hibernate对查询出的数据进行维护。
如果确实要同时使用Hibernate和Spring JDBC读写数据,则必须充分考虑到Hibernate缓存机制引发的问题:必须整体分析数据维护逻辑,根据需要及时调用Hibernate的flush()方法,以免覆盖Spring JDBC的更改,在Spring JDBC更改数据库时,维护Hibernate的缓存。由于方法调用顺序的不同都可能影响数据的同步性,因此很容易发生问题,这会极大提高数据访问程序的复杂性。所以笔者郑重建议不要同时使用Spring JDBC和Hibernate对数据进行写操作。
可以将以上结论推广到其他混合数据访问技术的方案中,如Hibernate+iBatis、JPA+SpringJDBC、JDO+Spring JDBC等。
http://blog.csdn.net/hy6688_/article/details/44783865
Spring事务管理--多个ORM框架在使用时的情况分析的更多相关文章
-
Spring事务管理入门与进阶
事务管理 一个数据库事务是一个单一的工作单元操作序列.这些操作应该要么完整地执行,要么完全不执行.事务管理是一个重要组成部分,事务管理可以确保数据完整性和一致性.事务具有以下四个关键特性分别是 ACI ...
-
Spring事务管理概述
以下内容引用自http://wiki.jikexueyuan.com/project/spring/transaction-management.html: 数据库事务是一个被视为单一的工作单元的操作 ...
-
框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)
一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...
-
[Spring框架]Spring 事务管理基础入门总结.
前言:在之前的博客中已经说过了数据库的事务, 不过那里面更多的是说明事务的一些锁机制, 今天来说一下Spring管理事务的一些基础知识. 之前的文章: [数据库事务与锁]详解一: 彻底理解数据库事务一 ...
-
spring事务管理器设计思想(一)
在最近做的一个项目里面,涉及到多数据源的操作,比较特殊的是,这多个数据库的表结构完全相同,由于我们使用的ibatis框架作为持久化层,为了防止每一个数据源都配置一套规则,所以重新实现了数据源,根据线程 ...
-
Spring事务管理器的应对
Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋.像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不太方便:而iBatis则通过模板化技术让你方便地控制SQ ...
-
Spring事务管理(转)
1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...
-
Spring 事务管理高级应用难点剖析--转
第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN& ...
-
【转】Spring事务管理
原文链接 在 Spring 中,事务是通过 TransactionDefinition 接口来定义的.该接口包含与事务属性有关的方法.具体如清单 1 所示: 清单 1. TransactionDefi ...
随机推荐
-
ZoomEye 钟馗之眼 搜索工具 基于API
#!/usr/bin/env python # -*- coding: utf-8 -*- # Author: f0rsaken import getopt import requests impor ...
-
It matters, In the coming year ,i will stand here.
人在安逸中会迷失自己,直至看不到远处的光亮.在一个人迷茫的时候便需要寻找认同感,于是我来到了这里.比我更加优秀的人都在默默的努力,我怎么能允许自己在原地踏步.在这一刻我似乎又看了黑夜中的一束光. 初识 ...
-
Javascript高级程序设计——BOM(浏览器对象模型)
BOM(浏览器对象模型),它提供了独立于内容而与浏览器窗口进行交互的对象.BOM由一系列相关的对象构成.一.window对象 window对象表示整个浏览器窗口,但不必表示其中包含的内容.W ...
-
Fedora 20下安装官方JDK替换OpenJDK并配置环境变量
Fedora 20自带OpenJDK,所以如果安装官方的JDK的话要先删除OpenJDK,步骤如下: 1:rpm -qa|grep jdk 查看当前的jdk情况. 2:yum -y remove ja ...
-
Android自定义TTF字体
前言: 在Android Design中一个设计手册.在设计手册中有常用的UI图标,图标大小规范等. 其中,有一个TTF字体,以前感觉没什么用.但是我在学习时,常看到有许多开发者使用Google 提供 ...
-
基于MySQL + Node.js + Leaflet的离线地图展示,支持百度、谷歌、高德、腾讯地图
1. 基本说明 本项目实现了离线展示百度.谷歌.高德.腾讯地图.主要功能如下: 实现了地图瓦片图下载.存储.目前支持存储至MySQL Node.js服务调用MySQL中的瓦片图 Leaflet展示地图 ...
-
2017OKR年终回顾与2018OKR初步规划
一.2017OKR - 年终回顾 自从6月份进行了年中总结,又是半年过去了,我的2017OKR又有了一些milestone.因此,按照国际惯例,又到了年终回顾的时候了,拉出来看看完成了多少.(以下目标 ...
-
js 变量的声明能提升 初始化不会提升
var x = 5; // 初始化 x elem = document.getElementById("demo"); // 查找元素 elem.innerHTML = x + & ...
-
Linux学习之常用命令(二)
1.上次介绍了一些常用的系统命令,这次又总结了一些小命令,故分享一下: 网卡地址查询的命令: ifconfig #不同于Windows系统,它的是ifconfig而不是ipconfig ip -a # ...
-
mysql外网链接
1:设置mysql的配置文件 /etc/mysql/my.cnf 找到 bind-address =127.0.0.1 将其注释掉://作用是使得不再只允许本地访问: 重启mys ...