分享
 
 
 

事务管理最佳实践多余的话之三Spring声明式事务管理出错示例与解决之道

王朝other·作者佚名  2006-12-01
窄屏简体版  字體: |||超大  

事务管理最佳实践多余的话之三

Spring声明式事务管理出错示例与解决之道

前言

今天,发现了一个以前写的使用Spring声明式事务管理的程序爆出了数据库连接错误,感觉是非常典型的一个误用Spring声明式事务管理的例子,拿出来为大家点评一下。

请先阅读我之前写的关于事务管理的文章:《事务管理最佳实践全面解析》, 《事务管理最佳实践多余的话之一“每次请求,一次数据库连接,一次事务”是不是金科玉律?》, 《事务管理最佳实践多余的话之二:Transaction后缀给声明式事务管理带来的好处》。

Spring声明式事务管理出错示例

这个应用程序是使用Spring管理的iBatis程序。事务使用了Spring的声明式事务管理。

爆出了如下的错误:

Caused by:

java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

at com.withub.wcms.manage.collectnews.systemNewsinfo.dao.WcmsSystemNewsinfoDao.add(WcmsSystemNewsinfoDao.java:50)

at com.withub.wcms.manage.collectnews.systemNewsinfo.service.WcmsSystemNewsinfoService.saveOrUpdate(WcmsSystemNewsinfoService.java:103)

at com.withub.wcms.manage.collectnews.systemNewsinfo.service.WcmsSystemNewsinfoService.formatRawInfoToHtml(WcmsSystemNewsinfoService.java:89)

可以看出,出现的错误是,Spring管理下的iBatis使用的数据库连接是只读的。而在DAO类的方法中,却使用了修改数据库的iBatis方法。

源代码:

WcmsSystemNewsinfoDao类的源代码就不列出来了。这个Dao类的add方法使用了iBatis的update方法,更新数据库数据。

下面是WcmsSystemNewsinfoService类的部分相关调用方法的源代码:

public String formatRawInfoToHtml(String infoRawId) throws Exception{

WcmsSystemNewsinfoRawModule wcmsSystemNewsinfoRawModule=new WcmsSystemNewsinfoRawModule();

wcmsSystemNewsinfoRawModule.setInfoId(infoRawId);

//原表数据

wcmsSystemNewsinfoRawModule=this.getManageNewsinfoService().queryRawInfoById(wcmsSystemNewsinfoRawModule);

WcmsSystemNewsinfoModule wcmsSystemNewsinfoModule=new WcmsSystemNewsinfoModule();

//目标表数据

BeanUtils.copyProperties(wcmsSystemNewsinfoModule, wcmsSystemNewsinfoRawModule);

wcmsSystemNewsinfoModule.setInfoRawId(wcmsSystemNewsinfoRawModule.getInfoId());

wcmsSystemNewsinfoModule.setInfoId(null);

returnthis.saveOrUpdate(wcmsSystemNewsinfoModule);

}

/**

*需要把目标表Model信息保存在目标表中。如果该记录已存在,则更新,否则,新增!

*

*@parammodule

*@throwsException

*/

public String saveOrUpdate(WcmsSystemNewsinfoModule module) throws Exception{

WcmsSystemNewsinfoModule loadModule=this.getWcmsSystemNewsinfoDao().selectWcmsSystemNewsinfoModuleByInfoRawId(module.getInfoRawId());

String infoId=null;

if(loadModule==null){

//插入

infoId=this.getWcmsSystemNewsinfoDao().add(module);

}else{

//更新

/**

*更新

*1,找到主键条件

*/

infoId=loadModule.getInfoId();

module.setInfoId(loadModule.getInfoId());

this.getWcmsSystemNewsinfoDao().updateProcessedInfo(module);

}

return infoId;

}

源代码说明:

WcmsSystemNewsinfoService类的formatRawInfoToHtml(String infoRawId)方法,根据传入的参数,准备好所需的数据,然后调用本类的saveOrUpdate(WcmsSystemNewsinfoModule module)方法。

saveOrUpdate()方法,根据情况,调用WcmsSystemNewsinfoDao类的add或者update方法。

Spring事务配置文件

一、事务配置抽象Bean声明

<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

<property name="transactionManager" ref="transactionManager"/>

<property name="transactionAttributes">

<props>

<prop key="*">readOnly</prop>

<prop key="add*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="modify*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="update*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="delete*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="remove*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="query*">PROPAGATION_REQUIRED, readOnly,-Exception</prop>

<prop key="load*">PROPAGATION_REQUIRED, -Exception</prop>

</props>

</property>

</bean>

二、服务类的Spring声明式事务管理

<!--

wcmsSystemNewsinfoService

-->

<bean id="wcmsSystemNewsinfoService"

parent="txProxyTemplate">

<property name="target">

<ref bean="wcmsSystemNewsinfoServiceTarget"/>

</property>

</bean>

错误解析

错误的原因就在上面的Spring声明式事务里。执行WcmsSystemNewsinfoService类的formatRawInfoToHtml()方法时,会应用txProxyTemplate的配置,由于它的方法名与所有的特殊配置都不匹配,因此,会应用第一个声明式事务:

<prop key="*">readOnly</prop>

因此,SpringAOP创建了一个只读的数据库连接和事务。

然后,调用WcmsSystemNewsinfoService类的saveOrUpdate()方法,也会查找上面的事务配置,匹配:

<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>

SpringAOP会去得到数据库连接和设置事务。由于在本地线程变量中已经找到了前面提供的只读连接,就会直接使用这个数据库连接,并在其上设置事务。

最后,调用WcmsSystemNewsinfoDao类的add方法。由于使用的是只读连接,执行add方法中的update语句,就发生了上面的错误:

Caused by:

java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

Spring真的能够管理一切吗?

像上面这样的Spring事务配置和Service、Dao的写法,应当说是相当普遍的,我们认为Spring已经把数据库连接、事务、O-R映射等等管得妥妥当当了,不用我们再操心了,再理解事务了!

真的如此吗?错!

漠视数据库、漠视事务,我们的系统到底有多少类似的隐患?我们的业务服务类Service浪费了多少SPringAOP的帮助?降低了多少性能?

Spring、EJB这样的声明式事务,确实大大方便了我们处理数据库连接和事务。但是,我们还是需要自己理解业务逻辑对数据库连接,对事务的需要!

工具只能帮助我们解决我们认识到的问题,解决不了我们都没理解的问题。

不能再把一切扔给框架、容器、工具!首先理解你的业务逻辑,理解你要实现的功能,然后搞清楚框架、容器、工具会帮助我们做什么。只有理解了自己的业务逻辑,理解了自己的代码,理解了自己要用到的第三方代码,才能真正完美地实现我们需要的功能!

用我们的命名方法来重构上面的问题代码

一、给Service类中的2个方法加上后缀名标识对事务的依赖

当然,Service接口相应的方法也要改变。

/* (non-Javadoc)

* @see com.withub.wcms.manage.collectnews.systemNewsinfo.service.IWcmsSystemNewsinfoService#formatRawInfoToHtml(java.lang.String)

*/

public String formatRawInfoToHtmlTransaction(String infoRawId) throws Exception{

WcmsSystemNewsinfoRawModule wcmsSystemNewsinfoRawModule=new WcmsSystemNewsinfoRawModule();

wcmsSystemNewsinfoRawModule.setInfoId(infoRawId);

//原表数据

wcmsSystemNewsinfoRawModule=this.getManageNewsinfoService().queryRawInfoById(wcmsSystemNewsinfoRawModule);

WcmsSystemNewsinfoModule wcmsSystemNewsinfoModule=new WcmsSystemNewsinfoModule();

//目标表数据

BeanUtils.copyProperties(wcmsSystemNewsinfoModule, wcmsSystemNewsinfoRawModule);

wcmsSystemNewsinfoModule.setInfoRawId(wcmsSystemNewsinfoRawModule.getInfoId());

wcmsSystemNewsinfoModule.setInfoId(null);

returnthis.saveOrUpdateDao(wcmsSystemNewsinfoModule);

}

/**

*需要把目标表Model信息保存在目标表中。如果该记录已存在,则更新,否则,新增!

*

*@parammodule

*@throwsException

*/

public String saveOrUpdateDao(WcmsSystemNewsinfoModule module) throws Exception{

WcmsSystemNewsinfoModule loadModule=this.getWcmsSystemNewsinfoDao().selectWcmsSystemNewsinfoModuleByInfoRawId(module.getInfoRawId());

String infoId=null;

if(loadModule==null){

//插入

infoId=this.getWcmsSystemNewsinfoDao().add(module);

}else{

//更新

/**

*更新

*1,找到主键条件

*/

infoId=loadModule.getInfoId();

module.setInfoId(loadModule.getInfoId());

this.getWcmsSystemNewsinfoDao().updateProcessedInfo(module);

}

return infoId;

}

这样,formatRawInfoToHtmlTransaction方法可以直接被最终用户调用。它将能够创建或得到数据库连接,管理事务,最后关闭数据库连接。

而,saveOrUpdateDao方法,不能直接被最终用户调用。如果它需要数据库连接,它可以使用本地线程变量中保存的数据库连接。

二、修改Service类的声明式事务的配置文件

<!--

wcmsSystemNewsinfoService

-->

<bean id="wcmsSystemNewsinfoService"

parent="txProxyTemplate">

<property name="target">

<ref bean="wcmsSystemNewsinfoServiceTarget"/>

</property>

<property name="transactionAttributes">

<props>

<prop key="*Transaction">PROPAGATION_REQUIRED,-Exception</prop>

</props>

</property>

</bean>

这里,重载了txProxyTemplate的声明式事务配置。我们只对Service类中的以Transaction结尾的方法,应用事务,在发生异常时,回滚。

这样,Transaction方法直接或者间接调用的DAO接口中的方法,就可以使用本地线程变量中保存的由Transaction方法的AOP代理方法创建的数据库连接。

数据库连接和事务被真正的摆平了!世界真美好!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有