精华帖 (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(); |
|
返回顶楼 | |
发表时间: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的时候实际上已经带着事务了的? |
|
返回顶楼 | |
发表时间:2007-06-06
这个问题估计就是osiv的问题拉,你的UserImpl有关联其他对象吧,而且也设置了延迟加载吧,session关了之后你去取UserImpl对象的关联对象,因为设置了延迟加载,所以这个关联对象其实是cglib增强过的一个增强类,如果你在session关闭之后去取它的非id属性肯定是要抛错的(取id没有关系,因为这个增强类的id是不为空的,而且就是UserImpl表中的id那个字段的值)
|
|
返回顶楼 | |
发表时间: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的方式,就是配置自动代理来实现,这两种方法在论坛上也已经出现过很多次了 |
|
返回顶楼 | |
发表时间: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 这么个类,所以转行错误了,这时候应该转行成什么呢? |
|
返回顶楼 | |
发表时间:2007-06-06
加上这个属性试试呢
<property name="proxyTargetClass"> <value>true</value> </property> org.springframework.transaction.interceptor.TransactionProxyFactoryBean这个类中可以用setProxyInterfaces(Class[] arg)或setProxyTargetClass来指定代理接口或代理类。 |
|
返回顶楼 | |
发表时间:2007-06-06
lujh99 写道 加上这个属性试试呢
<property name="proxyTargetClass"> <value>true</value> </property> 这下可以了,原来要指定代理的类,是不是用spring的这种注入 事务对象的方式的时候,必须指定 proxyTargetClass,否则是无法调用服务类的方法的,因为必须确定服务类的类型? |
|
返回顶楼 | |
发表时间: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更推荐的是面向接口编程 |
|
返回顶楼 | |
发表时间: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功能,这样方便吧? 不知道我的理解对不对? 但愿这样使不会带来性能问题就好! |
|
返回顶楼 | |
发表时间: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的方法 |
|
返回顶楼 | |