【spring框架26】spring使用XML进行声明式事务管理


d)xml(推荐,可以同时配置好多方法)
请看下面的接口和它的实现。这个例子的意图是介绍概念,使用 Foo 和 Bar 这样的名字只是为了让你关注于事务的用法,而不是领域模型。

// 我们想做成事务性的服务接口
 

package x.y.service;
 
public interface FooService {
 
  Foo getFoo(String fooName);
 
  Foo getFoo(String fooName, String barName);
 
  void insertFoo(Foo foo);
 
  void updateFoo(Foo foo);
 
}

// 上述接口的一个实现

package x.y.service;
 
public class DefaultFooService implements FooService {
 
  public Foo getFoo(String fooName) {
    throw new UnsupportedOperationException();
  }
 
  public Foo getFoo(String fooName, String barName) {
    throw new UnsupportedOperationException();
  }
 
  public void insertFoo(Foo foo) {
    throw new UnsupportedOperationException();
  }
 
  public void updateFoo(Foo foo) {
    throw new UnsupportedOperationException();
  }
 
 
}

(对该例的目的来说,上例中实现类(DefaultFooService)的每个方法在其方法体中抛出 UnsupportedOperationException 的做法是恰当的,我们可以看到,事务被创建出来, 响应 UnsupportedOperationException 的抛出,然后回滚。) 

我们假定,FooService的前两个方法(getFoo(String) 和getFoo(String, String))必须执行在只读事务上下文中,其他的方法(insertFoo(Foo)和 updateFoo(Foo))必须执行在可读写事务上下文中。不要想着一次理解下面的配置,所有内容都会在后面的章节详细讨论。
 

<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
  
  <!-- this is the service object that we want to make transactional -->
  <bean id="fooService" class="x.y.service.DefaultFooService"/>
 
  <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
  <tx:advice id="txAdvice" transaction-manager="txManager">
  <!-- the transactional semantics... -->
  <tx:attributes>
    <!-- all methods starting with 'get' are read-only -->
    <tx:method name="get*" read-only="true"/>
    <!-- other methods use the default transaction settings (see below) -->
    <tx:method name="*"/>
  </tx:attributes>
  </tx:advice>
  
  <!-- ensure that the above transactional advice runs for any execution
    of an operation defined by the FooService interface -->
  <aop:config>
  <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
  </aop:config>
  
  <!-- don't forget the DataSource -->
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
  <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
  <property name="username" value="scott"/>
  <property name="password" value="tiger"/>
  </bean>
 
  <!-- similarly, don't forget the PlatformTransactionManager -->
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
  </bean>
  
  <!-- other <bean/> definitions here -->
 
</beans>

我们来分析一下上面的配置。我们要把一个服务对象('fooService' bean)做成事务性的。 我们想施加的事务语义封装在<tx:advice/>定义中。<tx:advice/> “把所有以 'get' 开头的方法看做执行在只读事务上下文中, 其余的方法执行在默认语义的事务上下文中”。 其中的 'transaction-manager' 属性被设置为一个指向 PlatformTransactionManager bean的名字(这里指 'txManager'), 该bean将会真正管理事务。

提示
事实上,如果 PlatformTransactionManager bean的名字是 'transactionManager' 的话,你的事务通知(<tx:advice/>)中的 'transaction-manager' 属性可以忽略。否则你则需要像上例那样明确指定。

配置中最后一段是 <aop:config/> 的定义, 它确保由 'txAdvice' bean定义的事务通知在应用中合适的点被执行。 首先我们定义了 一个切面,它匹配 FooService 接口定义的所有操作, 我们把该切面叫做 'fooServiceOperation'。然后我们用一个通知器(advisor)把这个切面与 'txAdvice' 绑定在一起, 表示当 'fooServiceOperation' 执行时,'txAdvice' 定义的通知逻辑将被执行。

<aop:pointcut/> 元素定义是AspectJ的切面表示法,可参考Spring 2.0 第 6 章 使用Spring进行面向切面编程(AOP)章获得更详细的内容。

一个普遍性的需求是让整个服务层成为事务性的。满足该需求的最好方式是让切面表达式匹配服务层的所有操作方法。例如:
 

<aop:config>
  <aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
  </aop:config>

(这个例子中假定你所有的服务接口定义在 'x.y.service' 包中。你同样可以参考 第 6 章 使用Spring进行面向切面编程(AOP) 章获得更详细内容。) 

扫描二维码关注公众号,回复: 5113468 查看本文章

现在,既然我们已经分析了整个配置,你可能会问了,“好吧,但是所有这些配置做了什么?”。

上面的配置将为'fooService' bean创建一个代理对象,这个代理对象被装配了事务通知,所以当它的相应方法被调用时,一个事务将被启动、挂起、被标记为只读,或者其它(根据该方法所配置的事务语义)。

上面是官方的文档,下面我们自己测试一下
beans.xml:
 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
               
  <context:annotation-config/>
  <context:component-scan base-package="cn.edu.hpu"/>
 
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <value>classpath:com/foo/jdbc.properties</value>
    </property>
  </bean>
   
	<bean id="dataSource" destroy-method="close"
	      class="org.apache.commons.dbcp.BasicDataSource">
	    <property name="driverClassName" value="${jdbc.driverClassName}"/>
	    <property name="url" value="${jdbc.url}"/>
	    <property name="username" value="${jdbc.username}"/>
	    <property name="password" value="${jdbc.password}"/>
	</bean>
  
  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    	<property name="annotatedClasses">
      		<list>
        	<value>cn.edu.hpu.model.User</value>
        	<value>cn.edu.hpu.model.Log</value>
     		</list>
    	</property>
    	<property name="hibernateProperties">
			<props>
		   		<prop key="hibernate.dialect">
                    org.hibernate.dialect.MySQLDialect
                </prop>
		   		<prop key="hibernate.show_sql">true</prop>
			</props>
    	</property>
  	</bean>
  	<!-- 有关事务的配置 -->
  	<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
   		 <property name="sessionFactory" ref="sessionFactory"/>
	</bean>
	<!-- tx的Annotation配置 -->
	<!-- <tx:annotation-driven transaction-manager="txManager"/> -->
  	<aop:config>
  		<aop:pointcut expression="execution(public * cn.edu.hpu.service..*.*(..))" id="businessService"/>
  		<aop:advisor pointcut-ref="businessService" advice-ref="txAdvice"/>
  	</aop:config>
  	<tx:advice id="txAdvice" transaction-manager="txManager">
  		<tx:attributes>
    		    <tx:method name="get*" read-only="true"/>
   		    <tx:method name="add*" propagation="REQUIRED"/>
  	        </tx:attributes>
        </tx:advice>
</beans>

Service层:
UserService.java:

package cn.edu.hpu.service;
 
import javax.annotation.Resource;
 
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
 
import cn.edu.hpu.dao.LogDao;
import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.Log;
import cn.edu.hpu.model.User;
 
 
@Component("userService")
public class UserService {
 
	private UserDao userDao;
 
	public void init(){
		System.out.println("init");
	}
	
	public UserDao getUserDao() {
		return userDao;
	}
	
	@Resource(name="u")
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
	
	public LogDao getLogDao() {
		return logDao;
	}
	
	@Resource(name="logDao")
	public void setLogDao(LogDao logDao) {
		this.logDao = logDao;
	}
 
 
	public void add(User u){
		this.userDao.save(u);
		Log log=new Log();
		log.setMsg("a user saved!");
		this.logDao.save(log);
	}
	
	public void destroy(){
		System.out.println("destroy");
	}
	
}

UserDaoOmpl.java:

package cn.edu.hpu.dao.Impl;
import javax.annotation.Resource;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Component;
 
import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;
 
@Component("u")
public class UserDaoImpl implements UserDao{
	
	private SessionFactory sessionFactory;
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
 
 
	public void save(User u) {
		Session s=sessionFactory.getCurrentSession();
		s.save(u);
		System.out.println("add success!!");		
	}
}

测试:

package cn.edu.hpu.service;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import cn.edu.hpu.model.User;
 
public class UserServiceTest {
	
	@Test
	public void testAdd() throws Exception{
		ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
		
		UserService userService=(UserService)ctx.getBean("userService");
		System.out.println(userService.getClass());
		User u=new User();
		u.setName("jack");
		userService.add(u);
		ctx.destroy();
	}
}

测试结果:
class cn.edu.hpu.service.UserService $ $ EnhancerByCGLIB $ $ c43f0270
Hibernate: insert into User (name) values (?)
add success!!
Hibernate: insert into t_log (msg) values (?)

测试成功!

猜你喜欢

转载自blog.csdn.net/weixin_38213517/article/details/83027442