对一个对象加入spring事务

本文介绍了一种使用Hibernate框架处理Excel数据导入到数据库的方法,并解决了因字段长度限制导致的导入错误问题。通过配置Spring事务管理器实现了当导入过程中出现错误时能够回滚整个事务。

      由于项目里需要装excel里的数据导入到数据库中,本来一开始直接打算有jdbc来做的。直接生成insert语句的,但在后来发现有些字段实在太大了。而数据库中我将字段设置为nvarchar(2000)或varchar(4000)就不可以再增大了。用的是oracle数据库,不知道是什么原因,可能是甲公司出于性能方面的考虑吧!如果将字段改为bolb这样的话,sql又不知道如何写,哎...所以最后都系改为用hibernate算了,因为用这个我知道如何保存blob.

 

      用反射将每一行的数据保存至javabean里,然后就对javabean进行save操作,问题又出现了。因为excel表里,有些字段可以保存到数据库里,有些因为数据库字段长度的原因,保存时出错,因为excel里那个字段的长度估计有5-6K个字左右。唯有改成用blob这个了。问题是,因为前面的有些数据已经成功导入到数据库了。如果再重新导入的话就会变左有重复的数据了。hibernate报错只是将出错的那一条没有保存到数据库里,其它的都保存了。于是想如何才能确保如果有一条出错就全部不保存呢,第一时间想到的就是用事务了。但又不知如何做,上网搜也没有搜到比较满意的答案。于是就看了看spring技术手册。再测试一下,实现到我想要的效果了。为了不让自己忘记,将步骤写在这里,如果你有更好的办法,不妨话我知,我相信一定有的,只不过是我的理解能力差就知道这样可以实现我的要求罢了。

 

spring applictionContext.xml

<!--设置dataSource-->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="url" value="jdbc:mysql://localhost:3306/mywebspace?useUnicode=true&characterEncoding=utf8"></property>
		<property name="username" value="root"></property>
		<property name="password" value="790731"></property>
	</bean>
	
	<!--设置jdbcTemplate操作模板,用构造注入方式注入dataSource-->
	<bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate"> 
          <constructor-arg>   
               <ref bean="dataSource" />   
          </constructor-arg>  
   	</bean>   	  
  
    <!--
    	设置Hibernate对Lob的支持,lazy-int:延迟加载
		如果是oracle9i要改为org.springframework.jdbc.support.lob.OralceLobHandler
	-->
	<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true" />
	
	<!-- 
		设置SessionFactory,注入dataSource,lobHandler,	
		其中
		mappingDirectoryLocations:是设置一次性指定在某个路径下的所有.hbm.xml文件
		hibernate.dialect:         是表示将使用哪一种数据库产生SQL语句进行搞作
		hibernate.show_sql:         在后台打开Hibernate产生的SQL语句
	-->
	<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" >
	<property name="dataSource" ref="dataSource"></property>
	<property name="lobHandler" ref="lobHandler"></property>
	<property name="mappingDirectoryLocations">
		<list>
			<value>/WEB-INF/classes/com/mywebspace/bo</value>
		</list>
	</property>
	<property name="hibernateProperties">
		<props>
			<prop key="hibernate.dialect">
				<!-- 指定数据库方言 -->
				<!-- MSSQL2000 -->
				<!-- org.hibernate.dialect.SQLServerDialect -->
				<!-- MYSQL -->
				org.hibernate.dialect.MySQLDialect
				<!-- ORACLE -->
				<!-- org.hibernate.dialect.OracleDialect -->
			</prop>
			<prop key="hibernate.show_sql">true</prop> 
			<prop key="hibernate.jdbc.use_scrollable_resultset">false</prop>
	        <prop key="hibernate.use_outer_join">true</prop>      
	        <prop key="hibernate.jdbc.fetch_size">100</prop>
	        <prop key="hibernate.jdbc.batch_size">20</prop>
	        <prop key="hibernate.c3p0.min_size">20</prop>
		    <prop key="hibernate.c3p0.maxPoolSize">80</prop>
		    <prop key="hibernate.c3p0.max_statements">50</prop>
	        <prop key="hibernate.c3p0.timeout">1500</prop>
	        <!-- 查询语句里有中文时会出错乱码,解决方法加以下这行代码! -->
	        <prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
			
		</props>
	</property>	
	</bean>
	
	<!-- Hibernate事务,注入SessionFactory -->
	<bean id="myTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory">
			<ref local="mySessionFactory" />
		</property>
	</bean>
	

	<!-- 
	    事务处理的AOP配置(代理)
	    将Hibernate事务类注入,同时指定事务策略
	   transactionAttributes:指定事务策略
	   abstract="true"      :设置为抽象类,启动时不实例化
	-->
	<bean id="myProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="transactionManager">
			<ref local="myTransactionManager" />
		</property>
		<property name="transactionAttributes">
			<props>
				<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
				<prop key="insert*">PROPAGATION_REQUIRED</prop>
				<prop key="update*">PROPAGATION_REQUIRED</prop>
				<prop key="delete*">PROPAGATION_REQUIRED</prop>
				<prop key="save*">PROPAGATION_REQUIRED</prop>
				<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
				<prop key="init*">PROPAGATION_REQUIRED,readOnly</prop>
				<prop key="convert*">PROPAGATION_REQUIRED,readOnly</prop>
			</props>
		</property>
	</bean>
	

 

 

### Spring框架中同一类内方法间的事务传播机制 在Spring框架中,事务管理通过代理模式实现。对于同一个类内的方法调用,由于Java语言特性以及Spring AOP的工作原理,事务传播行为可能无法按预期生效。 #### 1. **事务传播机制的基础** Spring事务传播机制定义了多个包含事务方法在相互调用时,事务如何在这些方法间传递[^1]。例如,默认情况下`PROPAGATION_REQUIRED`表示如果没有现有事务则创建新事务;如果有,则加入已有事务[^4]。 然而,在同一个类内部的方法调用场景下,这种传播机制的行为会有所不同。 --- #### 2. **同类别方法调用的事务传播问题** 当一个带有`@Transactional`注解的方法A调用了同一个类中的另一个带`@Transactional`注解的方法B时,事务不会按照预期传播。这是因为Spring事务管理依赖于动态代理机制。具体来说: - 当外部客户端调用某个服务对象方法时,Spring会生成代理实例来拦截该调用并应用事务逻辑。 - 如果是在同一个类内直接调用其他方法(而非通过代理),那么Spring事务拦截器将不起作用[^3]。 以下是代码示例展示此现象: ```java @Service public class TransactionalService { @Transactional public void methodA() { try { methodB(); } catch (Exception e) { System.out.println("Method B failed, but Method A will not rollback."); } } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { // Simulate an exception to trigger rollback throw new RuntimeException("Simulated Exception"); } } ``` 在这个例子中,尽管`methodB()`标注了`REQUIRES_NEW`,但由于它是被`methodA()`直接调用而不是通过代理调用,因此其独立事务并未真正启动。 --- #### 3. **解决方案** 为了使事务能够在同一个类的不同方法之间正常传播,可以采用以下几种方案之一: ##### (1)**自我注入(Self-Injection)** 可以通过让Bean自己引用自身的方式来强制经过代理层。这样即使是从同一个类内部发起调用,也能触发事务拦截器。 修改后的代码如下所示: ```java @Service public class TransactionalService { @Autowired private TransactionalService self; @Transactional public void methodA() { try { self.methodB(); // 使用self代替this } catch (Exception e) { System.out.println("Method B failed, and now it should roll back properly."); } } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { // Simulate an exception to trigger rollback throw new RuntimeException("Simulated Exception"); } } ``` 这种方式虽然有效,但引入了一些额外复杂度,并且容易引发循环依赖等问题。 ##### (2)**提取公共接口或分离职责** 另一种更清晰的做法是将涉及不同业务逻辑的部分拆分到不同的服务组件中去。比如把原本属于单个类的功能划分成两个独立的服务类,从而自然形成跨类别的调用关系。 例如: ```java @Service public class ServiceOne { @Resource private ServiceTwo serviceTwo; @Transactional public void operationInClassOne() { serviceTwo.operationInAnotherClass(); } } @Service public class ServiceTwo { @Transactional(propagation = Propagation.REQUIRES_NEW) public void operationInAnotherClass() { // Some database operations here... } } ``` 上述设计不仅解决了事务传播难题,还促进了模块化开发原则的应用[^2]。 --- #### 4. **总结** 在同一类内进行方法调用时,Spring事务传播机制通常失效是因为缺少必要的代理介入过程。针对这种情况,开发者可以选择利用自我注入技术或者重构程序结构以规避此类局限性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值