当我使用在glassfish中使用Spring + JPA时,遇到若干问题,特记录备忘。
我的环境:glassfish 2.1 + JPA 1.0 + Spring 3.0.5
当我按照spring-framework-reference.pdf(3.0.5)中“13.5”的第二种方式,也就是从JNDI得到EntityManagerFactory实例这种方式来做时,结果是:
a. 部署没问题,但是当运行到程序中有db操作时,没有产生transaction,这样查询可以,persist动作全部无效,也不报错;
b. 如果在Dao类中加入了Spring的@Transactional,则persist时会报错。大意是在JPA环境下不能直接start transaction。
最终我也无法解决这个问题。我猜想改问题的根源在于:对于Spring来说,从JNDI得到EntityManagerFactory的实质,就是让容器来管理persist的事务,但是实际上从JNDI得到的这个EntityManagerFactory实例,似乎并没有相关的transactionManager,导致a出错;对于b来说,Spring在这种情况下处理@Transactional annotation的时候,似乎还是试图自己来创建事务,这和当前JPA环境下的事务管理发生了冲突,所以b也出错。
- 改变策略,尝试“13.5”的第三种方式,也就是使用LocalContainerEntityManagerFactoryBean,完全让Spring来管理JPA。这个过程中有个有趣的错误:
LocalContainerEntityManagerFactoryBean要求persistence.xml中的transaction-type是”RESOURCE_LOCAL”,但是这样设了之后部署war时glassfish会报错,说必须使用JPA类型;但是如果改成JPA类型,部署的时候glassfish不报错了,但Spring又会报错,说应该设成”RESOURCE_LOCAL”。
看到Spring reference里面有一个方法可以解决这个问题,就是写一个无用的persistence.xml来满足glassfish的校验要求,实际上使用的persistence.xml要改成另外一个名字,比如my-persistence.xml,然后在Spring的applicationContext.xml中引用my-persistence.xml。这样就可以解决问题。
总结:
Spring是很强大的,但是当它在application server环境下运行时,特别是运行JPA这种Spring和container都支持的技术时,Spring的功能有可能和container的功能产生冲突。我越来越觉得application server这种东西,很多时候是奇怪问题的根源,它的存在到底有多大意义呢?和单纯的web container相比,比如tomcat, jetty,我实在想不出它所谓的好处有什么绝对优势。实际情况中,有谁会在一个application server上部署多个ear, war, jar来跑?什么都想管的结果是问题多多,什么都管不好。和这种大而全的东西相比,我更喜欢小而精的功能单一的container。写到这里,再次想起unix的哲学:do one thing and do it best。