论坛首页 Java企业应用论坛

spring的事务不起作用?无法和当前sessin绑定?

浏览 25197 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-06-06  
刚才我看了一下源代码,找到一个解决此类问题的方法:
在TransactionAspectSupport类中有一个createTransactionIfNecessary方法,debug进去,只要查看TransactionAttribute这个对象的属性就知道事务配置是否成功了,请看TransactionAttribute的定义

/**
	 * Support a current transaction, create a new one if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>This is typically the default setting of a transaction definition.
	 */
	int PROPAGATION_REQUIRED = 0;

	/**
	 * Support a current transaction, execute non-transactionally if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>Note: For transaction managers with transaction synchronization,
	 * PROPAGATION_SUPPORTS is slightly different from no transaction at all,
	 * as it defines a transaction scopp that synchronization will apply for.
	 * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
	 * will be shared for the entire specified scope. Note that this depends on
	 * the actual synchronization configuration of the transaction manager.
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
	 */
	int PROPAGATION_SUPPORTS = 1;

	/**
	 * Support a current transaction, throw an exception if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
	int PROPAGATION_MANDATORY = 2;

	/**
	 * Create a new transaction, suspend the current transaction if one exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>Note: Actual transaction suspension will not work on out-of-the-box
	 * on all transaction managers. This in particular applies to JtaTransactionManager,
	 * which requires the <code>javax.transaction.TransactionManager</code> to be
	 * made available it to it (which is server-specific in standard J2EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	int PROPAGATION_REQUIRES_NEW = 3;

	/**
	 * Execute non-transactionally, suspend the current transaction if one exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>Note: Actual transaction suspension will not work on out-of-the-box
	 * on all transaction managers. This in particular applies to JtaTransactionManager,
	 * which requires the <code>javax.transaction.TransactionManager</code> to be
	 * made available it to it (which is server-specific in standard J2EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	int PROPAGATION_NOT_SUPPORTED = 4;

	/**
	 * Execute non-transactionally, throw an exception if a transaction exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
	int PROPAGATION_NEVER = 5;

	/**
	 * Execute within a nested transaction if a current transaction exists,
	 * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
	 * <p>Note: Actual creation of a nested transaction will only work on specific
	 * transaction managers. Out of the box, this only applies to the JDBC
	 * DataSourceTransactionManager when working on a JDBC 3.0 driver.
	 * Some JTA providers might support nested transactions as well.
	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
	 */
	int PROPAGATION_NESTED = 6;


	/**
	 * Use the default isolation level of the underlying datastore.
	 * All other levels correspond to the JDBC isolation levels.
	 * @see java.sql.Connection
	 */
	int ISOLATION_DEFAULT          = -1;

	/**
	 * A constant indicating that dirty reads, non-repeatable reads and phantom reads
	 * can occur. This level allows a row changed by one transaction to be read by
	 * another transaction before any changes in that row have been committed
	 * (a "dirty read"). If any of the changes are rolled back, the second
	 * transaction will have retrieved an invalid row.
	 * @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED
	 */
	int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

	/**
	 * A constant indicating that dirty reads are prevented; non-repeatable reads
	 * and phantom reads can occur. This level only prohibits a transaction
	 * from reading a row with uncommitted changes in it.
	 * @see java.sql.Connection#TRANSACTION_READ_COMMITTED
	 */
	int ISOLATION_READ_COMMITTED   = Connection.TRANSACTION_READ_COMMITTED;

	/**
	 * A constant indicating that dirty reads and non-repeatable reads are
	 * prevented; phantom reads can occur. This level prohibits a transaction
	 * from reading a row with uncommitted changes in it, and it also prohibits
	 * the situation where one transaction reads a row, a second transaction
	 * alters the row, and the first transaction rereads the row, getting
	 * different values the second time (a "non-repeatable read").
	 * @see java.sql.Connection#TRANSACTION_REPEATABLE_READ
	 */
	int ISOLATION_REPEATABLE_READ  = Connection.TRANSACTION_REPEATABLE_READ;

	/**
	 * A constant indicating that dirty reads, non-repeatable reads and phantom
	 * reads are prevented. This level includes the prohibitions in
	 * <code>ISOLATION_REPEATABLE_READ</code> and further prohibits the situation
	 * where one transaction reads all rows that satisfy a <code>WHERE</code>
	 * condition, a second transaction inserts a row that satisfies that
	 * <code>WHERE</code> condition, and the first transaction rereads for the
	 * same condition, retrieving the additional "phantom" row in the second read.
	 * @see java.sql.Connection#TRANSACTION_SERIALIZABLE
	 */
	int ISOLATION_SERIALIZABLE     = Connection.TRANSACTION_SERIALIZABLE;


	/**
	 * Use the default timeout of the underlying transaction system,
	 * or none if timeouts are not supported. 
	 */
	int TIMEOUT_DEFAULT = -1;


	/**
	 * Return the propagation behavior.
	 * Must return one of the PROPAGATION constants.
	 * @see #PROPAGATION_REQUIRED
	 * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive()
	 */
	int getPropagationBehavior();

	/**
	 * Return the isolation level.
	 * Must return one of the ISOLATION constants.
	 * <p>Only makes sense in combination with PROPAGATION_REQUIRED or
	 * PROPAGATION_REQUIRES_NEW.
	 * <p>Note that a transaction manager that does not support custom
	 * isolation levels will throw an exception when given any other level
	 * than ISOLATION_DEFAULT.
	 * @see #ISOLATION_DEFAULT
	 */
	int getIsolationLevel();

	/**
	 * Return the transaction timeout.
	 * Must return a number of seconds, or TIMEOUT_DEFAULT.
	 * <p>Only makes sense in combination with PROPAGATION_REQUIRED or
	 * PROPAGATION_REQUIRES_NEW.
	 * <p>Note that a transaction manager that does not support timeouts will
	 * throw an exception when given any other timeout than TIMEOUT_DEFAULT.
	 * @see #TIMEOUT_DEFAULT
	 */
	int getTimeout();

	/**
	 * Return whether to optimize as read-only transaction.
	 * This just serves as a hint for the actual transaction subsystem,
	 * it will <i>not necessarily</i> cause failure of write access attempts.
	 * <p>Intended to be used in combination with PROPAGATION_REQUIRED or
	 * PROPAGATION_REQUIRES_NEW. Beyond optimizing such actual transactions
	 * accordingly, a transaction manager might also pass the read-only flag
	 * to transaction synchronizations, even outside an actual transaction.
	 * <p>A transaction manager that cannot interpret the read-only hint
	 * will <i>not</i> throw an exception when given readOnly=true.
	 * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean)
	 * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()
	 */
	boolean isReadOnly();

	/**
	 * Return the name of this transaction. Can be <code>null</code>.
	 * This will be used as transaction name to be shown in a
	 * transaction monitor, if applicable (for example, WebLogic's).
	 * <p>In case of Spring's declarative transactions, the exposed name will
	 * be the fully-qualified class name + "." + method name (by default).
	 * @see org.springframework.transaction.interceptor.TransactionAspectSupport
	 * @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName()
	 */
	String getName();
0 请登录后投票
   发表时间:2007-06-06  
ahuaxuan 写道
引用
Object objBean=ctx.getBean("com.liferay.portlet.lyo.service.persistence.TestUtil");  

你取了一个没有被事务代理过的对象的引用,所以不能正常启动事务,确实应该取myProductService,也就是说应该取代理才对。


代码改成了:
org.springframework.context.ApplicationContext ctx
        = new org.springframework.context.support.ClassPathXmlApplicationContext(new String []{"META-INF/ext-spring-professional.xml","classpath*:META-INF/portal-spring-professional.xml","classpath*:META-INF/counter-spring-professional.xml"});
		Object objBean=ctx.getBean("myProductService");
		out.println("get bean: "+objBean);
com.liferay.portlet.lyo.service.persistence.TestUtil th=(com.liferay.portlet.lyo.service.persistence.TestUtil)objBean;
java.util.List list=th.testquery();


报错:
Session is closed
...........................................................


我把代码中的 hsession.close(); 去掉就可以了!

还从来没有这样用过 session,居然把一个配置事务使用的bean当作一个服务来用"myProductService".以前这个配置事务的bean myProductService 都是用来只是配置别的bean的事务拦截,从来没有直接在代码中使用过他,我可以看成是 myProductService 把我的bean "com.liferay.portlet.lyo.service.persistence.TestUtil" 封装了一层事务,我在调用 com.liferay.portlet.lyo.service.persistence.TestUtil的时候实际上已经带着事务了的?
0 请登录后投票
   发表时间:2007-06-06  
这个问题估计就是osiv的问题拉,你的UserImpl有关联其他对象吧,而且也设置了延迟加载吧,session关了之后你去取UserImpl对象的关联对象,因为设置了延迟加载,所以这个关联对象其实是cglib增强过的一个增强类,如果你在session关闭之后去取它的非id属性肯定是要抛错的(取id没有关系,因为这个增强类的id是不为空的,而且就是UserImpl表中的id那个字段的值)
0 请登录后投票
   发表时间:2007-06-06  
lyo 写道

还从来没有这样用过 session,居然把一个配置事务使用的bean当作一个服务来用"myProductService".以前这个配置事务的bean myProductService 都是用来只是配置别的bean的事务拦截,从来没有直接在代码中使用过他,我可以看成是 myProductService 把我的bean "com.liferay.portlet.lyo.service.persistence.TestUtil" 封装了一层事务,我在调用 com.liferay.portlet.lyo.service.persistence.TestUtil的时候实际上已经带着事务了的?

你在直接调用com.liferay.portlet.lyo.service.persistence.TestUtil的时候是不带事务的,必须调用代理才有事务属性。正是因为你调用了不带事务的TestUtil,所以在getCurrentSession() 的时候才会抛错,因为之前spring并没有创建session。
其实这属于spring配置事务的两种方式的一种,这种属于事务模板,你要调用的类一定是被显式的代理过的,还有一种是使用AOP的方式,就是配置自动代理来实现,这两种方法在论坛上也已经出现过很多次了
0 请登录后投票
   发表时间:2007-06-06  
liferay的做法都是操作数据库的类统一继承BasePersistence,这些子类直接 this.openSession()这样使用 hibernate Session.BasePersistence代码如下:
public class BasePersistence extends JdbcDaoSupport {

	public SessionFactory getSessionFactory() {
		return _sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		_sessionFactory = (SessionFactoryImplementor)sessionFactory;
		_dialect = _sessionFactory.getDialect();

		if (_log.isDebugEnabled()) {
			ConnectionProvider provider =
				_sessionFactory.getConnectionProvider();

			_log.debug("Connection provider " + provider.getClass().getName());
			_log.debug("Dialect " + _dialect.getClass().getName());
		}
	}

	protected Dialect getDialect() {
		return _dialect;
	}

	public void closeSession(Session session) {
		HibernateUtil.closeSession(session);
	}

	protected Session openSession() throws HibernateException {
		return HibernateUtil.openSession(_sessionFactory);
	}

	protected Session openSession(SessionFactory sessionFactory)
		throws HibernateException {

		return HibernateUtil.openSession(sessionFactory);
	}

	private static Log _log = LogFactory.getLog(BasePersistence.class);

	private SessionFactoryImplementor _sessionFactory;
	private Dialect _dialect;

}


我刚才试用我得TestUtil extends 这个BasePersistence,然后在 testquery方法直接 this.openSesion 不知道为什么不可以,当我运行测试代码的时候报错说类转型错误:
java.lang.ClassCastException
        at org.apache.jsp.html.portlet.login.view_jsp._jspService(view_jsp.java:
315)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:97)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
        at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper
.java:332)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:3
14)
        at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:264)


配置同时改成了这样:
<bean id="com.liferay.portlet.lyo.service.persistence.TestUtil" class="com.liferay.portlet.lyo.service.persistence.TestUtil" lazy-init="true">
		<property name="dataSource">
			<ref bean="liferayDataSource" />
		</property>
		<property name="sessionFactory">
			<ref bean="liferaySessionFactory" />
		</property>
	</bean>
<bean id="myProductService"
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true">
    <property name="transactionManager" ref="liferayTransactionManager"/>
    <property name="target">
      <ref bean="com.liferay.portlet.lyo.service.persistence.TestUtil"/>
    </property>
    <property name="transactionAttributes">
      <props>
        <prop key="test*">PROPAGATION_REQUIRED,readOnly</prop>
      </props>
    </property>
  </bean>

</beans>

这时候我的测试代码要求转型成 TestUtil,我用得到的bean对象的 getClass得到了对象,但是这个时候这个类好像变成了 $72Proxy 这么个类,所以转行错误了,这时候应该转行成什么呢?
0 请登录后投票
   发表时间:2007-06-06  
加上这个属性试试呢
<property name="proxyTargetClass">
    <value>true</value>
</property>

org.springframework.transaction.interceptor.TransactionProxyFactoryBean这个类中可以用setProxyInterfaces(Class[] arg)或setProxyTargetClass来指定代理接口或代理类。
0 请登录后投票
   发表时间:2007-06-06  
lujh99 写道
加上这个属性试试呢
<property name="proxyTargetClass">
    <value>true</value>
</property>


这下可以了,原来要指定代理的类,是不是用spring的这种注入 事务对象的方式的时候,必须指定 proxyTargetClass,否则是无法调用服务类的方法的,因为必须确定服务类的类型?
0 请登录后投票
   发表时间:2007-06-06  
如果象这样使用代理,那其实是隐式的使用了cglib再运行期动态创建的TestUtil类的子类作为代理类,但是俺觉得这样用会带来很多问题的,俺建议你使用jdk的proxy来对接口创建一个代理实现类。
public class TransactionProxyFactoryBean extends ProxyConfig
		implements FactoryBean, BeanFactoryAware, InitializingBean {

	private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

	private Object target;

	private Class[] proxyInterfaces;
}
从上代码可以看出,TransactionProxyFactoryBean 有一个proxyInterfaces属性,

<bean id="com.liferay.portlet.lyo.service.persistence.TestUtil" class="com.liferay.portlet.lyo.service.persistence.TestUtil" lazy-init="true">  
        <property name="dataSource">  
            <ref bean="liferayDataSource" />  
        </property>  
        <property name="sessionFactory">  
            <ref bean="liferaySessionFactory" />  
        </property>  
    </bean>  
<bean id="myProductService"  
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true">  
    <property name="transactionManager" ref="liferayTransactionManager"/>  
    <property name="target">  
      <ref bean="com.liferay.portlet.lyo.service.persistence.TestUtil"/>  
    </property>  
  <property name="proxyInterfaces">
<value>com.liferay.portlet.lyo.service.persistence.TestUtilInterface</value>
</property>
    <property name="transactionAttributes">  
      <props>  
        <prop key="test*">PROPAGATION_REQUIRED,readOnly</prop>  
      </props>  
    </property>  
  </bean>  

TestUtil实现TestUtilInterface就可以了

如果你要坚持cglib下去,那么

public class ProxyConfig implements Serializable {
	
	/*
	 * Note that some of the instance variables in this class and AdvisedSupport
	 * are protected, rather than private, as is usually preferred in Spring
	 * (following "Expert One-on-One J2EE Design and Development", Chapter 4).
	 * This allows direct field access in the AopProxy implementations, which
	 * produces a 10-20% reduction in AOP performance overhead compared with
	 * method access. - RJ, December 10, 2003.
	 */
	
	/**
	 * Transient to optimize serialization: AdvisedSupport resets it.
	 */
	protected transient Log logger = LogFactory.getLog(getClass());

	private boolean proxyTargetClass = false;
	
	private boolean optimize = false;
	
	private boolean opaque = false;
}
由此可见 lujh99 的解决方案也是正确的,因为TransactionProxyFactoryBean extends ProxyConfig。
从上面的代码可以看出spring的默认值是false,所以俺觉得spring更推荐的是面向接口编程
0 请登录后投票
   发表时间:2007-06-06  
引用
TestUtil实现TestUtilInterface就可以了
,TestUtilInterface 这个接口是哪里的?
你的意思是不是说我应该创建一个接口 ITestUtil,让这个TestUtil实现这个接口,然后在
<property name="proxyInterfaces">  
<value>com.liferay.portlet.lyo.service.persistence.ITestUtil</value>  
</property> 

我在代码中得到的那个 bean实际上是这个接口 ITestUtil接口,而不是TestUtil类? 不过这样作有些麻烦,spring比其他 aop的好处就是他能给非接口的类使用AOP功能,这样方便吧? 不知道我的理解对不对?

但愿这样使不会带来性能问题就好!
0 请登录后投票
   发表时间:2007-06-06  
lyo 写道
引用
TestUtil实现TestUtilInterface就可以了
,TestUtilInterface 这个接口是哪里的?
你的意思是不是说我应该创建一个接口 ITestUtil,让这个TestUtil实现这个接口,然后在
<property name="proxyInterfaces">  
<value>com.liferay.portlet.lyo.service.persistence.ITestUtil</value>  
</property> 

我在代码中得到的那个 bean实际上是这个接口 ITestUtil接口,而不是TestUtil类? 不过这样作有些麻烦,spring比其他 aop的好处就是他能给非接口的类使用AOP功能,这样方便吧? 不知道我的理解对不对?

但愿这样使不会带来性能问题就好!

不会有性能问题的,放心好了,这应该是spring推荐的使用aop的方法
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics