【发布时间】:2012-01-09 16:10:38
【问题描述】:
我一直在通过使用以下方式实现 JavaEE Web 应用程序来自学 Spring 3.0:
- 雄猫 7
- Spring 3.0、安全 3.1
- JPA:EclipseLink 2.3.1
- MySQL 5.5,连接器/J 5.1
我的问题是:
- 我已经使用 Spring AOP 在我的服务层上设置了声明式事务管理。
- (我还有一个 Spring AOP 日志拦截器,在同一个地方声明。)
- 创建新实体不是问题。
- 更新现有实体!
-
根据日志,服务方法在事务开始之前返回![org.springframework.orm.jpa.JpaTransactionManager] - <Using transaction object [org.springframework.orm.jpa.JpaTransactionManager$JpaTransactionObject@335f5e3a]> [org.springframework.orm.jpa.JpaTransactionManager] - <Creating new transaction with name [org.snowjak.livesavegive.data.service.TagService.rename]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT> [org.springframework.orm.jpa.JpaTransactionManager] - <Opened new EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@3a234c2f] for JPA transaction> [org.springframework.orm.jpa.JpaTransactionManager] - <Exposing JPA transaction as JDBC transaction [SimpleConnectionHandle: com.mysql.jdbc.JDBC4Connection@49116f61]> [myproject.data.service.MyEntityService] - <Beginning method: rename> [myproject.data.dao.MyEntityDao] - <Beginning method: get> [myproject.data.dao.MyEntityDao] - <After method: get> [myproject.data.dao.AbstractDao] - <Beginning method: update> [myproject.data.dao.AbstractDao] - <After method: update> [myproject.data.service.MyEntityService] - <After method: rename> [org.springframework.orm.jpa.JpaTransactionManager] - <Triggering beforeCommit synchronization> [org.springframework.orm.jpa.JpaTransactionManager] - <Triggering beforeCompletion synchronization> [org.springframework.orm.jpa.JpaTransactionManager] - <Initiating transaction commit> [org.springframework.orm.jpa.JpaTransactionManager] - <Committing JPA transaction on EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@3a234c2f]> [org.springframework.orm.jpa.JpaTransactionManager] - <Triggering afterCommit synchronization> [org.springframework.orm.jpa.JpaTransactionManager] - <Triggering afterCompletion synchronization> [org.springframework.orm.jpa.JpaTransactionManager] - <Closing JPA EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@3a234c2f] after transaction> 如果我将 DAO 设为事务性而不是服务层,则无法更新的问题就会消失。
- 但当局说那是不好的作风。
任何人都可以提供公会的任何指针/提示/技巧/秘密吗?
编辑:
我已经在我的记录器中为 org.springframework 开启了DEBUG detail;并看到,据我所知,事务毕竟得到了正确处理——即,在相关服务方法之前开始和之后结束。
我仍然有更新问题,即。我的更新没有出现在 PU 和 DB 中;现在为什么会这样? ...
编辑:
在尝试更新时,我还查看了 MySQL 通用日志。 JPA 似乎根本没有向数据库发出UPDATEs!
================================================ ====================================
实体:
-
在
myproject.data.entity下。没什么特别的。通用 JPA。@Entity public class MyEntity implements Serializable { @Id @GeneratedValue(strategy=GenerationType.AUTO) int id; @Column(nullable=false, unique=true) String name; ... }
DAO:
myproject.data.dao下的接口。
-
interface AbstractDao@Repository interface AbstractDao<T,K> extends Serializable { public T get(K key); public Collection<T> getAll(); public T save(T entity); public T update(T entity); } -
interface MyEntityDao@Repository interface MyEntityDao extends AbstractDao<MyEntity, Integer> { public MyEntity get(String name); }
在myproject.data.dao.jpa下实现。
-
abstract class AbstractDaoJpa@Repository abstract class AbstractDaoJpa implements AbstractDao<T,K> { protected EntityManager em; @PersistenceContext public void setEntityManager(EntityManager entityManager) { em = entityManager; } public T save(T entity) { em.persist(entity); return entity; } public T update(T entity) { return em.merge(entity); } } -
class MyEntityDaoJpa extends AbstractDaoJpa implements MyEntityDao@Repository class MyEntityDaoJpa extends AbstractDaoJpa<MyEntity,Integer> implements MyEntityDao { @Override public MyEntity get(Integer key) { return em.find(MyEntity.class, key); } @Override public MyEntity get(String name) { return em.createQuery("select E from MyEntity E where E.name = :name", MyEntity.class).setParameter("name", name).getSingleResult(); } ... }
服务层:
myproject.data.service下的接口
-
interface EntityService@Service public interface EntityService<T> extends Serializable { public Collection<T> getAll(); } -
interface MyEntityService extends EntityService@Service public interface MyEntityService extends EntityService<MyEntity> { public MyEntity create(String name); public MyEntity get(String name); public MyEntity rename(String name); ... }
在myproject.data.service.impl下实现
-
class MyEntityServiceImpl implements MyEntityService@Service public class MyEntityServiceImpl implements MyEntityService { private MyEntityDao dao; public MyEntityServiceImpl(MyEntityDao newDao) { dao = newDao; } @Override public MyEntity create(String name) { MyEntity e = new MyEntity(); e.name = name; ... return dao.save(e); } ... @Override public MyEntity rename(String oldName, String newName) { MyEntity e = dao.get(oldName); e.name = newName; dao.update(e); // An attempt to fix by explicitly merging the modified entity. // Didn't fix the problem. return e; } ... }
弹簧配置:
...
[ Declaration of DAOs and Service beans. Appropriate DAOs are injected into Service beans. ]
...
<!-- Tomcat-managed DataSource lookup via JNDI -->
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDS"/>
<!-- Configure the EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"/>
</property>
</bean>
<!-- Create the Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- Configure the Transaction Manager for AOP -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- Some methods are read-only. -->
<tx:method name="get*" read-only="true" propagation="SUPPORTS"/>
<!-- All others, use defaults. -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
...
<!-- Configure Spring AOP -->
<aop:config>
<aop:pointcut id="wholeProject" expression="within(myproject..*)"/>
<aop:pointcut id="serviceLayerOperation" expression="execution(* myproject.data.service.*.*(..))"/>
<!-- Configure transaction management. -->
<aop:advisor advice-ref="txAdvice" order="1" pointcut-ref="serviceLayerOperation"/>
<!-- Configure logging interceptor. -->
...
</aop:config>
【问题讨论】:
-
日志表明事务在方法之前开始(暴露JPA事务...)并在(提交JPA事务...之后提交)。你观察到什么行为,你期望什么行为?此外,您的 get 方法实现采用 Integer,而接口的方法采用 String。
-
你还没有向我们展示
public MyEntity get(String name);的实现 -
我的猜测是您正在重命名实体,其名称与其已有的名称相同。你为什么不向我们展示
public MyEntity get(String name);的实现? -
【不知道怎么从昨天开始就没有看到你们的cmets了!我很抱歉!]
public MyEntity get(String name)只是创建一个 JPA 查询来按名称检索持久的MyEntity。 -
我不是简单地分配与以前相同的名称。我已经使用 Eclipse 进行了跟踪,它看起来像
MyEntityService.rename()在重命名适当的MyEntity(在我的测试用例中从“nameA”到“name1”)之后返回; 但是事务提交后,什么都没有改变! (在 PU 或 DB 中)
标签: jpa eclipselink spring-aop spring-transactions