- 浏览: 270548 次
- 性别:
- 来自: 新乡
文章分类
- 全部博客 (227)
- servciemix (10)
- db (18)
- javaTools (4)
- hibernate (31)
- web (3)
- spring (14)
- design pattern (4)
- java security (3)
- portal (1)
- ejb (6)
- session (2)
- java_lang (21)
- jbpm (29)
- struts (7)
- orgRights (2)
- project manager Jira (7)
- 跨库事务 (2)
- mysql (14)
- ubuntu (7)
- osgi (9)
- maven ant make (4)
- 分布式 高并发 高性能 (5)
- virgo-dm_server (0)
- osgi web (3)
- platform (1)
- smooks (1)
- business (1)
- 职场生涯 (14)
- Java编码格式 (2)
- web服务 (1)
- 计算机使用 (1)
- 健康工作生活的保障,工作中务必抛掉的不良心态 (4)
- 电信-网络监控 (1)
- 多线程-multithread (1)
- 海量数据-高性能 (2)
- Mybatis (1)
- web开发平台研发 (0)
- oracle (0)
- 应用服务器调优 (0)
- web前端 (0)
- servlet-jsp (0)
- tomcat (2)
- newtouch (1)
- portal_liferay (2)
- version control (1)
- apm-impact (2)
- tools (1)
- 研发管理 (1)
- 电商业务 (1)
- 生鲜电商市场调查 (0)
- PBX (0)
- 房东 (0)
最新评论
-
lifuchao:
...
权限问题 -
Branding:
谢谢,受教了,另外,CONN AS SYSDBA,必须是在操作 ...
Oracle密码忘记了怎么办? -
zhuchao_ko:
...
Portal实现原理 -
败类斯文:
不知道改哪里。。。木有见到红色。。表示悟性低了、、
jira error: Neither the JAVA_HOME nor the JRE_HOME environment variable is defin -
c__06:
正文:假如事务我是这样定义的: <tx:method n ...
Spring中Transactional配置
spring下hibernate多数据库解决方案,以及跨库事务的尝试
相关文章:
spring下hibernate多数据库解决方案,以及跨库事务的尝试(二)
Spring+Hibernate组合使用时的奇怪问题
webwork2.2.1整合spring1.2.5遇到的问题
开发目的:一个协同平台项目,多托管用户,单门户系统,每个托管用户对应一个单一数据库,要求根据登陆用户的单位信息,自动选择操作数据库;同时,涉及跨库操作(比如跨库查询,跨库单据发送);同时事务处理必须支持这种多数据库模式,支持一些逻辑性不强的跨库事务,比如一些数据的发送和接收等<o:p></o:p>
当然,如果说跨库操作只涉及到数据的发送和接受的话,也可以通过构建专门web service以及通信线程来处理,<o:p></o:p>
开发环境: tomcat4.1,webwork<st1:chsdate month="12" islunardate="False" day="30" year="1899" w:st="on" isrocdate="False">2.2.4</st1:chsdate>,spring2.0.4,hibernate3.1,osworkflow2.8,mysql5.0.19 由于正式发布的应用服务器是weblogic8.1,所以没有采用jdk5环境以及struts2<o:p></o:p>
准备:<o:p></o:p>
问题一 由于有跨库操作,而且这种跨库操作无法预知,有的跨库操作完全是在逻辑运行中决定的,比如A托管用户或则C、D向B托管用户发了订单 ,B回复,这个回复是根据订单发送者来说的,具体到后台操作,是无法事先预知针对具体哪个后台物理数据库操作的.所以,也就是说,存在在业务执行过程中切换数据库的情况,传统的到注入设置dao类 sessionFactory、靠filter以及Interceptor设置线程安全的sessionFactory都无法完全达到设计目的<o:p></o:p>
问题二 事务,本来,我打算用JtaTransactionManager的,除了JtaTransactionManager,在开始时也实在没想到什么好的办法, 难道,JtaTransactionManager是唯一选择么?<o:p></o:p>
步骤:<o:p></o:p>
、 因为问题一,所以系统在资源方面是多sessionFactory并存的方式,也就是多少个托管用户多少个sessionFactory,当然,事实上,这种应用型项目本身并发访问量不会太高(什么,很高么,总比不过广告联盟吧,哈哈).不用担心多了几个sessionFactory会对系统或则数据库造成多大影响.<o:p></o:p>
<o:p>
xml 代码
<bean id="mitDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="jdbcUrl">
<value>jdbc:mysql://127.0.0.1:3306/mitflow</value>
</property>
<property name="user">
<value>root</value>
</property>
...........................
</bean>
<bean id="mitSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="mappingResources">
..................................
</props>
</property>
<property name="dataSource">
<ref local="mitDataSource" />
</property>
</bean>
然后复制,粘贴,修改jdbcUrl,象网站编辑一样..
假设,我配置了两个sessionFatory ,一个是mitSessionFactory,一个是testSessionFactory,如下:
xml 代码
<hibernatesupport>
<item id="mit" bean="mitSessionFactory"/>
<item id="test" bean="testSessionFactory"/>
< hibernatesupport>
这个我自己系统配置的一部分,系统会解析他,从而知晓究竟存在多少个sessionFactory,item's XmlNode中的id可以理解会托管客户的客户单位<o:p></o:p>
编号,当然,这个配置完全可以忽略,直接从ApplicationContext中一样可以获取到这样的信息<o:p></o:p>
在客户登陆的时候,系统要记录下该客户所属托管单位,然后通过上面的id找到bean's name ,最后获取这个sessionFactory,托管单位信息一般<o:p></o:p>
都是个编号而已跟己方系统的托管用户管理相结合,一般是保存这个编号在session里面,也可以象asp.net一样,记录在安全凭证里,还不知道JAVA方面有没有类似实现,个人认为asp.net这个方法很值得采用,虽然MS号称安全系数+++++这个观点值得怀疑<o:p></o:p>
<o:p> </o:p>
首先建立一个类,HibernateSupport ,存放当前请求线程所需sessionFactory
java 代码
public class HibernateSupport {
public static final String HIBERNATE_SESSIONIDKEY = "com.mit.hibernatesupport.factory.id";
private static final Logger logger = Logger.getLogger(HibernateSupport.class);
private static ApplicationContext applicationContext ;
private static boolean singleSession=true;
private static Map factorybeanset;
private static ThreadLocal switchhistory;//在切换不同sessionFactory的时候用到
private static ThreadLocal idset;//记录当前默认的托管用户id,在实际使用中,这个是可以取消掉的
private static ThreadLocal curfactory;////当前正在使用的sessionFactory
private static ThreadLocal trace;//一个sessionFactory集合,用来记录这次线程调用了那些sessionFactory
static
{
idset = new ThreadLocal();
curfactory = new ThreadLocal();
trace = new ThreadLocal();
switchhistory = new ThreadLocal();
}
/**
* set current sessionfactory for the Request
* @param ServletContext
* @param the factory's id defined in courser.xml
*/
public static synchronized void setCurrent(ServletContext context,Object id)
{
if (idset.get()==null)
{
idset.set(id);
if (factorybeanset.containsKey(id))
{
if (applicationContext==null)
{
applicationContext =
WebApplicationContextUtils
.getWebApplicationContext(context);
}
curfactory.set((SessionFactory)applicationContext
.getBean((String)factorybeanset.get(id)));
putTrace(idset.get(),(SessionFactory)curfactory.get());
}
}
}
/**
* put the sessionfactory to tracemap
* @see COPenSessionInViewFilter release sessionfactory in tracemap
* @param the factory's id defined in courser.xml
* @param hibernate's sessionfactory
*/
private static void putTrace(Object id ,SessionFactory factory)
{
Map tracemap = null;
if (trace.get()==null)
{
tracemap = new HashMap();
trace.set(tracemap);
}
else
{
tracemap = (Map)trace.get();
}
if (!tracemap.containsKey(id))
{
tracemap.put(id, factory);
}
}
/**
* switch current sessionfactory
* @param the factory's id defined in courser.xml
*/
public static synchronized void swtichFactory(Object id)
{
if (!idset.get().equals(id) )
{
if (factorybeanset.containsKey(id))
{
SessionFactory oldfactory = (SessionFactory)curfactory.get();
SessionFactory newfactory = (SessionFactory)applicationContext
.getBean((String)factorybeanset.get(id));
curfactory.set(newfactory);
pushHistory(oldfactory);
putTrace(id,newfactory);
bindSessionFactory(newfactory);
}
}
}
/**
* restore sessionfactory from queue of switchhistory
*/
public static synchronized void restoreFactory()
{
SessionFactory factory = popHistory();
if (factory!=null)
{
curfactory.set(factory);
}
}
/**
* push old sessionfactory to swithhistory after swtichFactory
* @param hibernate's sessionfactory
*/
private static void pushHistory(SessionFactory sessionfactory)
{
LinkedList list = null;
if (switchhistory.get()==null)
{
list = new LinkedList();
switchhistory.set(list);
}
else
{
list = (LinkedList)switchhistory.get();
}
list.add(0,sessionfactory);
}
/**
* pop sessionfactory in queue
*/
private static SessionFactory popHistory()
{
if (switchhistory.get()!=null)
{
LinkedList list = (LinkedList)switchhistory.get();
if (list.size()>0)
{
SessionFactory factory = (SessionFactory)list.getFirst();
list.removeFirst();
return factory;
}
}
return null;
}
public static Map getTraceMap()
{
if (trace.get()!=null)
{
return (Map)trace.get();
}
return null;
}
public static SessionFactory getCurrentFactory()
{
return (SessionFactory)curfactory.get();
}
public static synchronized void release()
{
idset.set(null);
curfactory.set(null);
switchhistory.set(null);
trace.set(null);
}
/**
* °ó¶¨sessionFactoryµ½springµÄ×ÊÔ´¹ÜÀí
* @param hibernate's sessionfactory
*/
private static synchronized boolean bindSessionFactory(SessionFactory sessionFactory)
{
boolean participate=false;;
if (singleSession) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
}
else {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
if (!TransactionSynchronizationManager.hasResource(sessionFactory))
{
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
}
}
else {
// deferred close mode
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
}
else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
return participate;
}
//see SessionFactoryUtils
private static Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
FlushMode flushMode = FlushMode.COMMIT;
if (flushMode != null) {
session.setFlushMode(flushMode);
}
return session;
}
public static synchronized void initSessionFactory(Map res,Class loadclass)
{
factorybeanset =res;
}
}
HibernateSupport这个类其他方法可以不管,暂时关注setCurrent这个方法
java 代码
if (idset.get()==null)
{
idset.set(id);
if (factorybeanset.containsKey(id)) //factorybeanset包含的就是我自己系统配置中那一部分,key就是id,,value就是sessionFactory 在spring环境中的beanName
{
if (applicationContext==null)
{
applicationContext =WebApplicationContextUtils.getWebApplicationContext(context);
}
curfactory.set((SessionFactory)applicationContext.getBean((String)factorybeanset.get(id)));//设置当前的sessionFactory
putTrace(idset.get(),(SessionFactory)curfactory.get());//put到当前线程的一个记录集
}
}
然后,就要修改spring关于hibernate的一些支持类了,当然,也可以选择重新写一套dao支持类,呵呵,不过,显然,在spring基础上做一些小修改代价更小<o:p></o:p> HibernateAccessor (HibernateTemplate的基类)以及HibernateTransactionManager都是靠注入方式获取一个sessionFactory,显然,这套不适合了,修改之
多sessionFactory好做,配置在spring或则单独拿出来处理都可以,但是spring的HibernateDaoSupport 必须绑定一个sessionFactory,当然,我们完全可以写一个自己的HibernateDaoSupport ,但是既然用了spring的事务管理而又不想多花时间,还是将就改改用吧
java 代码
private SessionFactory sessionFactory;
publicvoid setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
<o:p></o:p>
去掉HibernateAccessor 和HibernateTransactionManager中的上述两段代码,当然,也别忘了毙掉两个类中的<o:p></o:p>
afterPropertiesSet方法中那些检查代码<o:p></o:p>
然后 ,ant打包就可以了,如果不想修改spring的代码,也可以单独把这几个类提出来另建jar包,我是单独提出来新建的,比如HibernateTransactionManager我改名成CHibernateTransactionManager,其他类似,但是包名必须是org.springframework.orm.hibernate3 ,谁也不想这么做,可是谁让sessionFactoryUtils中一个closexxxx方法没定义成public了??<o:p></o:p>
如果想变更sessionFactoryUtils,奉劝算了吧..<o:p></o:p>
然后可以做测试了,首先,部署测试的dao和service,先是事务部署<o:p></o:p>
xml 代码
<bean id="transactionManager"
class="com.mit.web.hibernate.CHibernateTransactionManager"/>
<bean id="transres" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="load*">PROPAGATION_REQUIRED,readOnlyprop>
<prop key="save*">PROPAGATION_REQUIREDprop>
<prop key="delete*">PROPAGATION_REQUIREDprop>
<prop key="find*">PROPAGATION_REQUIRED,readOnlyprop>
<prop key="query*">PROPAGATION_REQUIRED,readOnlyprop>
<prop key="create*">PROPAGATION_REQUIREDprop>
<prop key="set*">PROPAGATION_REQUIRED,readOnlyprop>
<prop key="execute*">PROPAGATION_REQUIREDprop>
props>
property>
bean>
<bean id="transactionInterceptor" class=
"其实,也有解决方法,就是延迟提交,具体实现方式以后讲述"
以下存属个人理解:
其实JtaTransactionManager做的正是这一点, 在spring中通过TransactionSynchronizer来实现的:service.save()并不会commit,而是将当前Transaction放入一个ThreadLocal的Transaction list里,到结束时一起处理.
的确,我的确是按照Transaction list这个思路做的,通过新建一个PlatformTransactionManager类,大部分参照AbstractPlatformTransactionManager的内容,只是在这两个类的commit和rollback里面做点控制就可以了
返回顶楼
hsqldb里的transaction是row transaction
就像command pattern的undo,而且还有一个MEMENTO,入在log里.
dist transaction的机制应该差不多,只是加了个message queue
事务配置如下:
<bean id="transactionManager"
class="com.mit.web.hibernate.CHibernateTransactionManager"/>
<bean id="transres" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="set*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="execute*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributeSource"><ref local="transres"/></property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames"><value>*Service</value></property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>
我把事务配置在service层,事务管理采用的是我修改后的HibernateTransactionManager ,他不在要求sessionFactory的注入了
以下是测试的dao类和service类配置
Java代码 收藏代码
<bean id="personDao" class="com.mit.web.action.PersonDaoImpl"/>
<bean id="personService" class="com.mit.web.action.PersonServiceImpl">
<property name="personDao">
<ref bean="personDao"/>
</property>
</bean>
<bean id="departDao" class="com.mit.web.action.DepartDaoImpl"/>
<bean id="departService" class="com.mit.web.action.DepartServiceImpl">
<property name="personService">
<ref bean="personService"/>
</property>
<property name="departDao">
<ref bean="departDao"/>
</property>
</bean>
具体代码不写了,大概如下:
public class PersonDaoImpl extends CHibernateDaoSupport implements PersonDao;
public class PersonServiceImpl implements PersonService;
public class DepartDaoImpl extends CHibernateDaoSupport implements DepartDao;
public class DepartServiceImpl implements DepartService;
测试代码的目的是将两个实体类分别存放到mit和test库,假设默认用户是mit这个托管库的,这个方法就写在DepartServiceImpl类里面
Java代码 收藏代码
public class DepartServiceImpl implements DepartService {
private DepartDao dao;
private PersonService service;
..............................
public void executeTest(Depart depart,Person person)
{
dao.save(depart);
HibernateSupport.switchFactory("test");
service.save(person);
HibernateSupport.restoreFactory();
}
..............................
上面代码中将depart对象存到mit库,而将person对象存到test库中.
事务 事务 ,没看见特别事务设置? 其实事务已经在运行了
如前面配置,事务我配置在了service层,那么当executeTest执行时,会启动针对mit库的事务(前面我们改了HibernateTransactionManager的sessionFactory获取方式),
dao.save(depart);//然后保存depart到mit库
HibernateSupport.switchFactory("test");//切换库到test
service.save(person);//调用personService的save方法,这也是个service,所以也会产生一个事务,而此时HibernateSupport.getCurrentFactory返回的sessionFactory已经是test库的了,所以,spring事务管理发现上下文中并没有针对test的事务,于是,会重新启动一个新的事务,这个service.save(person);方法执行完后,这个事务将会提交
HibernateSupport.restoreFactory();//切换回默认数据库
整个方法执行完后,针对mit的事务将会被提交
[color=green]如果service.save(person);发生异常,这两个事务就会被提交,一个简单跨库事务控制完成[color=green]
但是,问题也随之而来,如果在 HibernateSupport.restoreFactory();后,又进行了一些逻辑操作,那么发生异常时,而由于 service.save(person);这个事务已经被提交,也就是说,针对test的事务已经提交不会回滚了,这是个非常严重的问题。。
其实,也有解决方法,就是延迟提交,具体实现方式以后讲述
跨库事务之延迟提交
延迟事务,就是将事务延后提交,延迟的时间由事务管理器掌握。在我的系统中,只有跨库操作涉及到延迟提交,针对这种操作,我设计了一个基本的执行模型。就是如果一个逻辑中存在多个事务,将全部放到逻辑执行完以后提交,那么,既然如此,开始吧
PlatformTransactionManager是spring平台相关事务,比如HibernateTransactionManager都是继承于此类,为了达到延迟提交的目的,可以在AbstractPlatformTransactionManagershang上做修改达到目的
首先,说一下在spring中,通常的一个事务流程,
流程如下:初始化Transaction B,如果发现前面有其他Transaction ,比如 Transaction A,那么挂起TransactionA ,然后启动事务 ,当逻辑执行完后 ,commit,恢复挂起事务A,然后清除同步对象以及其他资源,如果执行发生异常,当然,异常发生后 rollback
延迟提交的设计思想是将事务都暂存在一个threadlocal的LIST里面,等逻辑执行完以后,再统一提交,那么首先在AbstractPlatformTransactionManager中设置一个threadlocal对象
private ThreadLocal lazytrace = new ThreadLocal();
LazyTransactionStatus用来管理需要延迟提交的事务
private static class LazyTransactionStatus
{
private java.util.List transliat;
private int transnum;//代表未提交事务数量
public LazyTransactionStatus()
{
transliat= new java.util.ArrayList();
transnum=0;
}
public void newLazy()
{
transnum++;
}
public void push(TransactionStatus trasobj)
{
objmap.add(trasobj);
}
public void removeLazy(TransactionStatus trasobj)
{
transnum--;
}
public boolean canCommit()
{
if (transnum<1)
{
return true;
}
else
return false;
}
public java.util.List getTransactionObjs()
{
return transliat;
}
}
这就是local对象的存储内容.translist存放当前执行中的TransactionStatus实例
TransactionStatus顾名思义,事务状态,包含了事务、挂起资源等一系列信息
然后以事务提交为例
然后在AbstractTransactionManager增加如下两个方法
Java代码 收藏代码
public final boolean isCommit(TransactionStatus status)
{
if (lazytrace.get()!=null)
{
LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();
lazystatus.removeLazy(status);
return lazy.canCommit();
}
return true;
}
protected void begin(Object transaction, TransactionDefinition definition)
{
doBegin(transaction,definition);
LazyTransactionStatus lazystatus = null;
if (lazytrace.get()==null)
{
lazystatus = new LazyTransactionStatus();
lazytrace.set(lazystatus);
}
else
{
lazystatus = (LazyTransactionStatus)lazytrace.get();
}
lazystatus.newLazy();
}
public final void registerTraceStatus(TransactionStatus status)
{
LazyTransactionStatus lazystatus = null;
if (lazytrace.get()==null)
{
lazystatus = new LazyTransactionStatus();
lazytrace.set(lazystatus);
}
else
{
lazystatus = (LazyTransactionStatus)lazytrace.get();
}
lazystatus.push(status);
}
begin ,当一个事务开始时,将LazyTransactionStatus的transnum+1,表示又多了个刚开始,还未提交的事务
registerTraceStatus发生在commit的时候,注册这个事务到LazyTransactionStatus,同时,
注意 transnum表示的是未提交事务数量,所以当事务管理器执行commit表示要提交一个事务后,transnum将减一,如果减一后发现transnum<1,表示所有事务都提交了,那么,将所有事务提交。否则,不提交,继续等待...
关系如下:
begin->transnum+1 表示新事务诞生
registerTraceStatus(发生在commit的时候)->将准备提交的TransStatus放到LazyTransactionStatus,是的,这个事务要提交了,来吧,先注册一下
紧接着
isCommit()->将transnum-1,如果发现transnum小于1 ,表示闹够了,可以都提交滚蛋了
注意 ,transnum与LazyTransactionStatus的translist的链表长度在执行commit的时候是反方向发展的 一个增,一个减
好了,首先是注册事务数量,不用管了,在事务开始时begin方法它自己会调用了,
然后是修改AbstractPlatformTransactionManager的commit方法
Java代码 收藏代码
public final void commit(TransactionStatus txstatus) throws TransactionException {
this.registerTraceStatus(txstatus);
if (this.isCommit(txstatus))
{
int error = 0;
LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();
List statuslist = lazystatus.getTransactionObjs();
for (int i=0;i<statuslist.size();i++)
{
try
{
TransactionStatus status = (TransactionStatus)statuslist.get(i);
if (status.isCompleted()) {
error++;
continue;
//throw new IllegalTransactionStateException(
// "Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);
error++;
continue;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
//throw new UnexpectedRollbackException(
//"Transaction rolled back because it has been marked as rollback-only");
error++;
continue;
}
continue;
}
processCommit(defStatus);
}
catch (Exception ex)
{
error++;
ex.printStackTrace();
continue;
}
}
lazytrace.set(null);
if (error>0)
throw new IllegalTransactionStateException(
"Not commit all transaction");
}
}
this.registerTraceStatus(txstatus);//事务提交了,成了嫌疑犯,拖到threadlocal的LazyTransactionStatus监狱里面先关起来
if (isCommit()) //看看监狱的事务是不是满了,如果满了,就可以全放了
LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();
List statuslist = lazystatus.getTransactionObjs();
for (int i=0;i<statuslist.size();i++)
{
........
processCommit(defStatus);
//看来真的满了,都放了吧
回滚道理是一样的,不过不用判断了,直接全部放出来让他们滚吧
当然,目前这个实现只是个模型,真要实际应用,还需要做进一步封装,实际做,我用了OpenSessionInViewFilter,我已经做过测试,测试了了OpenSessionInViewFilter中singleSession为true和false两种情况,测试都通过了,呵呵
相关文章:
spring下hibernate多数据库解决方案,以及跨库事务的尝试(二)
Spring+Hibernate组合使用时的奇怪问题
webwork2.2.1整合spring1.2.5遇到的问题
开发目的:一个协同平台项目,多托管用户,单门户系统,每个托管用户对应一个单一数据库,要求根据登陆用户的单位信息,自动选择操作数据库;同时,涉及跨库操作(比如跨库查询,跨库单据发送);同时事务处理必须支持这种多数据库模式,支持一些逻辑性不强的跨库事务,比如一些数据的发送和接收等<o:p></o:p>
当然,如果说跨库操作只涉及到数据的发送和接受的话,也可以通过构建专门web service以及通信线程来处理,<o:p></o:p>
开发环境: tomcat4.1,webwork<st1:chsdate month="12" islunardate="False" day="30" year="1899" w:st="on" isrocdate="False">2.2.4</st1:chsdate>,spring2.0.4,hibernate3.1,osworkflow2.8,mysql5.0.19 由于正式发布的应用服务器是weblogic8.1,所以没有采用jdk5环境以及struts2<o:p></o:p>
准备:<o:p></o:p>
问题一 由于有跨库操作,而且这种跨库操作无法预知,有的跨库操作完全是在逻辑运行中决定的,比如A托管用户或则C、D向B托管用户发了订单 ,B回复,这个回复是根据订单发送者来说的,具体到后台操作,是无法事先预知针对具体哪个后台物理数据库操作的.所以,也就是说,存在在业务执行过程中切换数据库的情况,传统的到注入设置dao类 sessionFactory、靠filter以及Interceptor设置线程安全的sessionFactory都无法完全达到设计目的<o:p></o:p>
问题二 事务,本来,我打算用JtaTransactionManager的,除了JtaTransactionManager,在开始时也实在没想到什么好的办法, 难道,JtaTransactionManager是唯一选择么?<o:p></o:p>
步骤:<o:p></o:p>
、 因为问题一,所以系统在资源方面是多sessionFactory并存的方式,也就是多少个托管用户多少个sessionFactory,当然,事实上,这种应用型项目本身并发访问量不会太高(什么,很高么,总比不过广告联盟吧,哈哈).不用担心多了几个sessionFactory会对系统或则数据库造成多大影响.<o:p></o:p>
<o:p>
xml 代码
<bean id="mitDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="jdbcUrl">
<value>jdbc:mysql://127.0.0.1:3306/mitflow</value>
</property>
<property name="user">
<value>root</value>
</property>
...........................
</bean>
<bean id="mitSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="mappingResources">
..................................
</props>
</property>
<property name="dataSource">
<ref local="mitDataSource" />
</property>
</bean>
然后复制,粘贴,修改jdbcUrl,象网站编辑一样..
假设,我配置了两个sessionFatory ,一个是mitSessionFactory,一个是testSessionFactory,如下:
xml 代码
<hibernatesupport>
<item id="mit" bean="mitSessionFactory"/>
<item id="test" bean="testSessionFactory"/>
< hibernatesupport>
这个我自己系统配置的一部分,系统会解析他,从而知晓究竟存在多少个sessionFactory,item's XmlNode中的id可以理解会托管客户的客户单位<o:p></o:p>
编号,当然,这个配置完全可以忽略,直接从ApplicationContext中一样可以获取到这样的信息<o:p></o:p>
在客户登陆的时候,系统要记录下该客户所属托管单位,然后通过上面的id找到bean's name ,最后获取这个sessionFactory,托管单位信息一般<o:p></o:p>
都是个编号而已跟己方系统的托管用户管理相结合,一般是保存这个编号在session里面,也可以象asp.net一样,记录在安全凭证里,还不知道JAVA方面有没有类似实现,个人认为asp.net这个方法很值得采用,虽然MS号称安全系数+++++这个观点值得怀疑<o:p></o:p>
<o:p> </o:p>
首先建立一个类,HibernateSupport ,存放当前请求线程所需sessionFactory
java 代码
public class HibernateSupport {
public static final String HIBERNATE_SESSIONIDKEY = "com.mit.hibernatesupport.factory.id";
private static final Logger logger = Logger.getLogger(HibernateSupport.class);
private static ApplicationContext applicationContext ;
private static boolean singleSession=true;
private static Map factorybeanset;
private static ThreadLocal switchhistory;//在切换不同sessionFactory的时候用到
private static ThreadLocal idset;//记录当前默认的托管用户id,在实际使用中,这个是可以取消掉的
private static ThreadLocal curfactory;////当前正在使用的sessionFactory
private static ThreadLocal trace;//一个sessionFactory集合,用来记录这次线程调用了那些sessionFactory
static
{
idset = new ThreadLocal();
curfactory = new ThreadLocal();
trace = new ThreadLocal();
switchhistory = new ThreadLocal();
}
/**
* set current sessionfactory for the Request
* @param ServletContext
* @param the factory's id defined in courser.xml
*/
public static synchronized void setCurrent(ServletContext context,Object id)
{
if (idset.get()==null)
{
idset.set(id);
if (factorybeanset.containsKey(id))
{
if (applicationContext==null)
{
applicationContext =
WebApplicationContextUtils
.getWebApplicationContext(context);
}
curfactory.set((SessionFactory)applicationContext
.getBean((String)factorybeanset.get(id)));
putTrace(idset.get(),(SessionFactory)curfactory.get());
}
}
}
/**
* put the sessionfactory to tracemap
* @see COPenSessionInViewFilter release sessionfactory in tracemap
* @param the factory's id defined in courser.xml
* @param hibernate's sessionfactory
*/
private static void putTrace(Object id ,SessionFactory factory)
{
Map tracemap = null;
if (trace.get()==null)
{
tracemap = new HashMap();
trace.set(tracemap);
}
else
{
tracemap = (Map)trace.get();
}
if (!tracemap.containsKey(id))
{
tracemap.put(id, factory);
}
}
/**
* switch current sessionfactory
* @param the factory's id defined in courser.xml
*/
public static synchronized void swtichFactory(Object id)
{
if (!idset.get().equals(id) )
{
if (factorybeanset.containsKey(id))
{
SessionFactory oldfactory = (SessionFactory)curfactory.get();
SessionFactory newfactory = (SessionFactory)applicationContext
.getBean((String)factorybeanset.get(id));
curfactory.set(newfactory);
pushHistory(oldfactory);
putTrace(id,newfactory);
bindSessionFactory(newfactory);
}
}
}
/**
* restore sessionfactory from queue of switchhistory
*/
public static synchronized void restoreFactory()
{
SessionFactory factory = popHistory();
if (factory!=null)
{
curfactory.set(factory);
}
}
/**
* push old sessionfactory to swithhistory after swtichFactory
* @param hibernate's sessionfactory
*/
private static void pushHistory(SessionFactory sessionfactory)
{
LinkedList list = null;
if (switchhistory.get()==null)
{
list = new LinkedList();
switchhistory.set(list);
}
else
{
list = (LinkedList)switchhistory.get();
}
list.add(0,sessionfactory);
}
/**
* pop sessionfactory in queue
*/
private static SessionFactory popHistory()
{
if (switchhistory.get()!=null)
{
LinkedList list = (LinkedList)switchhistory.get();
if (list.size()>0)
{
SessionFactory factory = (SessionFactory)list.getFirst();
list.removeFirst();
return factory;
}
}
return null;
}
public static Map getTraceMap()
{
if (trace.get()!=null)
{
return (Map)trace.get();
}
return null;
}
public static SessionFactory getCurrentFactory()
{
return (SessionFactory)curfactory.get();
}
public static synchronized void release()
{
idset.set(null);
curfactory.set(null);
switchhistory.set(null);
trace.set(null);
}
/**
* °ó¶¨sessionFactoryµ½springµÄ×ÊÔ´¹ÜÀí
* @param hibernate's sessionfactory
*/
private static synchronized boolean bindSessionFactory(SessionFactory sessionFactory)
{
boolean participate=false;;
if (singleSession) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
}
else {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
if (!TransactionSynchronizationManager.hasResource(sessionFactory))
{
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
}
}
else {
// deferred close mode
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
}
else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
return participate;
}
//see SessionFactoryUtils
private static Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
FlushMode flushMode = FlushMode.COMMIT;
if (flushMode != null) {
session.setFlushMode(flushMode);
}
return session;
}
public static synchronized void initSessionFactory(Map res,Class loadclass)
{
factorybeanset =res;
}
}
HibernateSupport这个类其他方法可以不管,暂时关注setCurrent这个方法
java 代码
if (idset.get()==null)
{
idset.set(id);
if (factorybeanset.containsKey(id)) //factorybeanset包含的就是我自己系统配置中那一部分,key就是id,,value就是sessionFactory 在spring环境中的beanName
{
if (applicationContext==null)
{
applicationContext =WebApplicationContextUtils.getWebApplicationContext(context);
}
curfactory.set((SessionFactory)applicationContext.getBean((String)factorybeanset.get(id)));//设置当前的sessionFactory
putTrace(idset.get(),(SessionFactory)curfactory.get());//put到当前线程的一个记录集
}
}
然后,就要修改spring关于hibernate的一些支持类了,当然,也可以选择重新写一套dao支持类,呵呵,不过,显然,在spring基础上做一些小修改代价更小<o:p></o:p> HibernateAccessor (HibernateTemplate的基类)以及HibernateTransactionManager都是靠注入方式获取一个sessionFactory,显然,这套不适合了,修改之
多sessionFactory好做,配置在spring或则单独拿出来处理都可以,但是spring的HibernateDaoSupport 必须绑定一个sessionFactory,当然,我们完全可以写一个自己的HibernateDaoSupport ,但是既然用了spring的事务管理而又不想多花时间,还是将就改改用吧
java 代码
private SessionFactory sessionFactory;
publicvoid setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
<o:p></o:p>
去掉HibernateAccessor 和HibernateTransactionManager中的上述两段代码,当然,也别忘了毙掉两个类中的<o:p></o:p>
afterPropertiesSet方法中那些检查代码<o:p></o:p>
然后 ,ant打包就可以了,如果不想修改spring的代码,也可以单独把这几个类提出来另建jar包,我是单独提出来新建的,比如HibernateTransactionManager我改名成CHibernateTransactionManager,其他类似,但是包名必须是org.springframework.orm.hibernate3 ,谁也不想这么做,可是谁让sessionFactoryUtils中一个closexxxx方法没定义成public了??<o:p></o:p>
如果想变更sessionFactoryUtils,奉劝算了吧..<o:p></o:p>
然后可以做测试了,首先,部署测试的dao和service,先是事务部署<o:p></o:p>
xml 代码
<bean id="transactionManager"
class="com.mit.web.hibernate.CHibernateTransactionManager"/>
<bean id="transres" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="load*">PROPAGATION_REQUIRED,readOnlyprop>
<prop key="save*">PROPAGATION_REQUIREDprop>
<prop key="delete*">PROPAGATION_REQUIREDprop>
<prop key="find*">PROPAGATION_REQUIRED,readOnlyprop>
<prop key="query*">PROPAGATION_REQUIRED,readOnlyprop>
<prop key="create*">PROPAGATION_REQUIREDprop>
<prop key="set*">PROPAGATION_REQUIRED,readOnlyprop>
<prop key="execute*">PROPAGATION_REQUIREDprop>
props>
property>
bean>
<bean id="transactionInterceptor" class=
"其实,也有解决方法,就是延迟提交,具体实现方式以后讲述"
以下存属个人理解:
其实JtaTransactionManager做的正是这一点, 在spring中通过TransactionSynchronizer来实现的:service.save()并不会commit,而是将当前Transaction放入一个ThreadLocal的Transaction list里,到结束时一起处理.
的确,我的确是按照Transaction list这个思路做的,通过新建一个PlatformTransactionManager类,大部分参照AbstractPlatformTransactionManager的内容,只是在这两个类的commit和rollback里面做点控制就可以了
返回顶楼
hsqldb里的transaction是row transaction
就像command pattern的undo,而且还有一个MEMENTO,入在log里.
dist transaction的机制应该差不多,只是加了个message queue
事务配置如下:
<bean id="transactionManager"
class="com.mit.web.hibernate.CHibernateTransactionManager"/>
<bean id="transres" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="set*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="execute*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributeSource"><ref local="transres"/></property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames"><value>*Service</value></property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>
我把事务配置在service层,事务管理采用的是我修改后的HibernateTransactionManager ,他不在要求sessionFactory的注入了
以下是测试的dao类和service类配置
Java代码 收藏代码
<bean id="personDao" class="com.mit.web.action.PersonDaoImpl"/>
<bean id="personService" class="com.mit.web.action.PersonServiceImpl">
<property name="personDao">
<ref bean="personDao"/>
</property>
</bean>
<bean id="departDao" class="com.mit.web.action.DepartDaoImpl"/>
<bean id="departService" class="com.mit.web.action.DepartServiceImpl">
<property name="personService">
<ref bean="personService"/>
</property>
<property name="departDao">
<ref bean="departDao"/>
</property>
</bean>
具体代码不写了,大概如下:
public class PersonDaoImpl extends CHibernateDaoSupport implements PersonDao;
public class PersonServiceImpl implements PersonService;
public class DepartDaoImpl extends CHibernateDaoSupport implements DepartDao;
public class DepartServiceImpl implements DepartService;
测试代码的目的是将两个实体类分别存放到mit和test库,假设默认用户是mit这个托管库的,这个方法就写在DepartServiceImpl类里面
Java代码 收藏代码
public class DepartServiceImpl implements DepartService {
private DepartDao dao;
private PersonService service;
..............................
public void executeTest(Depart depart,Person person)
{
dao.save(depart);
HibernateSupport.switchFactory("test");
service.save(person);
HibernateSupport.restoreFactory();
}
..............................
上面代码中将depart对象存到mit库,而将person对象存到test库中.
事务 事务 ,没看见特别事务设置? 其实事务已经在运行了
如前面配置,事务我配置在了service层,那么当executeTest执行时,会启动针对mit库的事务(前面我们改了HibernateTransactionManager的sessionFactory获取方式),
dao.save(depart);//然后保存depart到mit库
HibernateSupport.switchFactory("test");//切换库到test
service.save(person);//调用personService的save方法,这也是个service,所以也会产生一个事务,而此时HibernateSupport.getCurrentFactory返回的sessionFactory已经是test库的了,所以,spring事务管理发现上下文中并没有针对test的事务,于是,会重新启动一个新的事务,这个service.save(person);方法执行完后,这个事务将会提交
HibernateSupport.restoreFactory();//切换回默认数据库
整个方法执行完后,针对mit的事务将会被提交
[color=green]如果service.save(person);发生异常,这两个事务就会被提交,一个简单跨库事务控制完成[color=green]
但是,问题也随之而来,如果在 HibernateSupport.restoreFactory();后,又进行了一些逻辑操作,那么发生异常时,而由于 service.save(person);这个事务已经被提交,也就是说,针对test的事务已经提交不会回滚了,这是个非常严重的问题。。
其实,也有解决方法,就是延迟提交,具体实现方式以后讲述
跨库事务之延迟提交
延迟事务,就是将事务延后提交,延迟的时间由事务管理器掌握。在我的系统中,只有跨库操作涉及到延迟提交,针对这种操作,我设计了一个基本的执行模型。就是如果一个逻辑中存在多个事务,将全部放到逻辑执行完以后提交,那么,既然如此,开始吧
PlatformTransactionManager是spring平台相关事务,比如HibernateTransactionManager都是继承于此类,为了达到延迟提交的目的,可以在AbstractPlatformTransactionManagershang上做修改达到目的
首先,说一下在spring中,通常的一个事务流程,
流程如下:初始化Transaction B,如果发现前面有其他Transaction ,比如 Transaction A,那么挂起TransactionA ,然后启动事务 ,当逻辑执行完后 ,commit,恢复挂起事务A,然后清除同步对象以及其他资源,如果执行发生异常,当然,异常发生后 rollback
延迟提交的设计思想是将事务都暂存在一个threadlocal的LIST里面,等逻辑执行完以后,再统一提交,那么首先在AbstractPlatformTransactionManager中设置一个threadlocal对象
private ThreadLocal lazytrace = new ThreadLocal();
LazyTransactionStatus用来管理需要延迟提交的事务
private static class LazyTransactionStatus
{
private java.util.List transliat;
private int transnum;//代表未提交事务数量
public LazyTransactionStatus()
{
transliat= new java.util.ArrayList();
transnum=0;
}
public void newLazy()
{
transnum++;
}
public void push(TransactionStatus trasobj)
{
objmap.add(trasobj);
}
public void removeLazy(TransactionStatus trasobj)
{
transnum--;
}
public boolean canCommit()
{
if (transnum<1)
{
return true;
}
else
return false;
}
public java.util.List getTransactionObjs()
{
return transliat;
}
}
这就是local对象的存储内容.translist存放当前执行中的TransactionStatus实例
TransactionStatus顾名思义,事务状态,包含了事务、挂起资源等一系列信息
然后以事务提交为例
然后在AbstractTransactionManager增加如下两个方法
Java代码 收藏代码
public final boolean isCommit(TransactionStatus status)
{
if (lazytrace.get()!=null)
{
LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();
lazystatus.removeLazy(status);
return lazy.canCommit();
}
return true;
}
protected void begin(Object transaction, TransactionDefinition definition)
{
doBegin(transaction,definition);
LazyTransactionStatus lazystatus = null;
if (lazytrace.get()==null)
{
lazystatus = new LazyTransactionStatus();
lazytrace.set(lazystatus);
}
else
{
lazystatus = (LazyTransactionStatus)lazytrace.get();
}
lazystatus.newLazy();
}
public final void registerTraceStatus(TransactionStatus status)
{
LazyTransactionStatus lazystatus = null;
if (lazytrace.get()==null)
{
lazystatus = new LazyTransactionStatus();
lazytrace.set(lazystatus);
}
else
{
lazystatus = (LazyTransactionStatus)lazytrace.get();
}
lazystatus.push(status);
}
begin ,当一个事务开始时,将LazyTransactionStatus的transnum+1,表示又多了个刚开始,还未提交的事务
registerTraceStatus发生在commit的时候,注册这个事务到LazyTransactionStatus,同时,
注意 transnum表示的是未提交事务数量,所以当事务管理器执行commit表示要提交一个事务后,transnum将减一,如果减一后发现transnum<1,表示所有事务都提交了,那么,将所有事务提交。否则,不提交,继续等待...
关系如下:
begin->transnum+1 表示新事务诞生
registerTraceStatus(发生在commit的时候)->将准备提交的TransStatus放到LazyTransactionStatus,是的,这个事务要提交了,来吧,先注册一下
紧接着
isCommit()->将transnum-1,如果发现transnum小于1 ,表示闹够了,可以都提交滚蛋了
注意 ,transnum与LazyTransactionStatus的translist的链表长度在执行commit的时候是反方向发展的 一个增,一个减
好了,首先是注册事务数量,不用管了,在事务开始时begin方法它自己会调用了,
然后是修改AbstractPlatformTransactionManager的commit方法
Java代码 收藏代码
public final void commit(TransactionStatus txstatus) throws TransactionException {
this.registerTraceStatus(txstatus);
if (this.isCommit(txstatus))
{
int error = 0;
LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();
List statuslist = lazystatus.getTransactionObjs();
for (int i=0;i<statuslist.size();i++)
{
try
{
TransactionStatus status = (TransactionStatus)statuslist.get(i);
if (status.isCompleted()) {
error++;
continue;
//throw new IllegalTransactionStateException(
// "Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);
error++;
continue;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
//throw new UnexpectedRollbackException(
//"Transaction rolled back because it has been marked as rollback-only");
error++;
continue;
}
continue;
}
processCommit(defStatus);
}
catch (Exception ex)
{
error++;
ex.printStackTrace();
continue;
}
}
lazytrace.set(null);
if (error>0)
throw new IllegalTransactionStateException(
"Not commit all transaction");
}
}
this.registerTraceStatus(txstatus);//事务提交了,成了嫌疑犯,拖到threadlocal的LazyTransactionStatus监狱里面先关起来
if (isCommit()) //看看监狱的事务是不是满了,如果满了,就可以全放了
LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();
List statuslist = lazystatus.getTransactionObjs();
for (int i=0;i<statuslist.size();i++)
{
........
processCommit(defStatus);
//看来真的满了,都放了吧
回滚道理是一样的,不过不用判断了,直接全部放出来让他们滚吧
当然,目前这个实现只是个模型,真要实际应用,还需要做进一步封装,实际做,我用了OpenSessionInViewFilter,我已经做过测试,测试了了OpenSessionInViewFilter中singleSession为true和false两种情况,测试都通过了,呵呵
相关推荐
总结起来,这个多数据库解决方案利用Spring的IoC容器管理和配置了多个数据源,同时结合Hibernate提供了ORM功能。通过`OpenSessionInView`模式,确保了Web请求中的持久化操作能在合适的Session上下文中进行。这样的...
### 基于Spring与Hibernate的数据库访问技术研究 #### 引言 在现代企业级应用开发...在未来,随着技术的不断发展,Spring与Hibernate的集成将会有更多的创新和优化,为开发者带来更加便捷和强大的数据访问解决方案。
"spring+hibernate和spring+myBatis实现连接多个数据库,同时操作的项目"是针对这种需求的一个解决方案,旨在提供一种灵活且动态的数据源切换机制。 首先,Spring框架作为Java领域中最受欢迎的应用框架之一,其强大...
Spring、Hibernate和JTA(Java Transaction API)是Java开发者在构建分布式事务解决方案时常用的三大技术。本示例“第二部分spring+hibernate+jta 分布式事务Demo”将帮助我们理解如何在这三个框架之间协同工作,...
4. 分布式事务:如果涉及多个数据库或服务,可能需要使用分布式事务解决方案,如X/Open XA或两阶段提交协议。 本实例通过Spring、Hibernate和MySQL的整合,展示了如何在实际应用中实现事务处理,保证了数据的一致性...
总的来说,"spring-hibernate3.jar"是一个便捷的集成解决方案,它使得Spring和Hibernate的配合更为紧密,提高了开发效率。但理解其背后的工作原理和配置方法,对于开发者来说同样重要,这将有助于他们更好地运用这两...
标题"spring+hibernate"指的是将这两个框架结合使用,形成一个强大的Java开发解决方案。Spring通过其Transaction Management(事务管理)模块与Hibernate集成,能够提供声明式事务处理,这大大简化了事务控制的复杂...
Spring提供了强大的依赖注入(DI)和面向切面编程(AOP),而Hibernate则是一个优秀的对象关系映射(ORM)解决方案,使得开发者可以方便地操作数据库。当我们需要在应用程序中进行事务管理时,Spring提供了一种声明...
首先,Spring框架是一个全面的后端解决方案,它提供了依赖注入(DI)和面向切面编程(AOP)的核心功能。在全注解模式下,我们可以避免XML配置,直接在类或方法上使用注解来声明bean及其依赖关系。例如,`@Component`...
Spring适合大型、复杂的应用,因为它提供了全面的解决方案,包括数据库访问、事务管理、安全等。Hibernate则适用于需要ORM特性的项目,简化了对象和数据库的交互。而iBatis对于那些需要高度控制SQL或者关心性能的...
Spring、Hibernate和Atomikos的组合就是一种强大的解决方案,它们可以协同工作以支持多数据源的分布式事务处理。接下来,我们将深入探讨这些技术以及如何配置它们来实现多数据源。 1. **Spring**: Spring是一个...
Hibernate作为Java持久化层的解决方案,通过对象关系映射(ORM)技术,将数据库操作转化为对象操作,使得开发者可以专注于业务逻辑,而无需过多关注底层数据库细节。 整合ZK、Spring和Hibernate的过程主要分为以下...
总的来说,Spring和Hibernate的整合是Java Web开发中的常见实践,它结合了Spring的灵活控制和Hibernate的强大持久化能力,为开发者提供了高效、易维护的解决方案。通过学习和实践这个整合示例,你可以更好地理解和...
Spring3和Hibernate3的整合是企业级Java应用开发中的常见实践,旨在提供一个高效、灵活且可维护的解决方案。下面将详细阐述这两个框架的基本概念以及整合过程中的关键知识点。 Spring是一个全面的后端开发框架,它...
Spring框架是一个全面的Java应用程序开发框架,它提供了一种依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)的解决方案。Spring的核心特性可以用于任何Java应用,而Spring...
Spring Boot简化了Spring的应用启动和配置,而Spring Cloud则为微服务架构提供了全套解决方案。 Hibernate框架: Hibernate是一个优秀的对象关系映射(ORM)框架,它消除了Java开发者与数据库之间的直接交互,将...
在"Spring Hibernate Ext"项目中,Spring和Hibernate被紧密集成,实现了一种数据访问层的解决方案。Spring通过其DataSource和Transaction Manager组件来管理数据库连接和事务,同时通过HibernateTemplate或...
Spring作为一个全面的框架,提供了一种轻量级的控制反转(IoC)和面向切面编程(AOP)的解决方案;Hibernate则是一个强大的对象关系映射(ORM)工具,简化了数据库操作;而MySQL则是一款广泛使用的开源关系型数据库...