我有一个问题,如果我的服务上有一个客户端调用两个方法,它就会失败,因为第二个html" target="_blank">方法中的事务没有与之关联的会话。但是,如果我将这两种方法组合到服务中,并从客户机代码中调用其中一种方法,它就会成功。
谁能给我解释一下为什么会这样?
考虑下面的代码:
@Configurable
public class ParentService {
@PersistenceContext
private EntityManager entityManager;
public ParentService() {
}
@Transactional
public Parent findById( Long id ) {
return entityManager.findById( Parent.class, id );
}
@Transactional
public Set<Child> getChildrenFor( Parent parent ) {
return Collections.unmodifiableSet( new HashSet<>( parent.getChildren() ) );
}
@Transactional
public Set<Child> getChildrenFor( Long id ) {
Parent parent = findById( id );
return getChildrenFor( parent );
}
...
}
所以这里发生的事情是,在我的客户机代码中(它不知道事务),如果我调用#getChildrenFor(id),我就没事了。但如果我打电话:
Parent parent = service.findById( id );
Set<Child> children = service.getChildrenOf( parent );
然后hibernate抛出一个异常,表示没有与当前事务关联的会话,因此它无法遍历延迟加载的PersistentSet#getChildren。
现在我不是JPA或Spring专家,所以这可能是有意的行为。如果是,你能告诉我为什么吗?我是否需要创建一个不是服务要公开的实体的DTA,然后让我的客户机使用它而不是实体?我认为,由于两个调用都使用相同的实体管理器引用,因此客户端应该能够进行两个调用。
顺便说一句,这是使用CTW的“Spring配置”。JpaTransactionManager配置用于交叉切割,如下所示:
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txnMgr = new JpaTransactionManager();
txnMgr.setEntityManagerFactory( entityManagerFactory().getObject() );
// cross cut transactional methods with txn management
AnnotationTransactionAspect.aspectOf().setTransactionManager( txnMgr );
return txnMgr;
}
请让我知道我可以提供的任何其他信息,以帮助解决此问题。
Spring XML配置:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<context:spring-configured/>
<context:component-scan base-package="com.myapp"/>
</beans>
Spring Java配置:
@Configuration
@PropertySource( "classpath:database.properties" )
public class DatabaseConfiguration {
@Value( "${database.dialect}" )
private String databaseDialect;
@Value( "${database.url}" )
private String databaseUrl;
@Value( "${database.driverClassName}" )
private String databaseDriver;
@Value( "${database.username}" )
private String databaseUser;
@Value( "${database.password}" )
private String databasePassword;
@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName( "persistenceUnit" );
factory.setDataSource( dataSource() );
Properties props = new Properties();
props.setProperty( "hibernate.dialect", databaseDialect );
factory.setJpaProperties( props );
return factory;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txnMgr = new JpaTransactionManager();
txnMgr.setEntityManagerFactory( entityManagerFactory().getObject() );
// cross cut transactional methods with txn management
AnnotationTransactionAspect.aspectOf().setTransactionManager( txnMgr );
return txnMgr;
}
@Bean
public DataSource dataSource() {
final BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName( databaseDriver );
dataSource.setUrl( databaseUrl );
dataSource.setUsername( databaseUser );
dataSource.setPassword( databasePassword );
dataSource.setTestOnBorrow( true );
dataSource.setTestOnReturn( true );
dataSource.setTestWhileIdle( true );
dataSource.setTimeBetweenEvictionRunsMillis( 1800000 );
dataSource.setNumTestsPerEvictionRun( 3 );
dataSource.setMinEvictableIdleTimeMillis( 1800000 );
return dataSource;
}
}
POM:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version>
<!-- NB: do not use 1.3 or 1.3.x due to MASPECTJ-90 and do not use 1.4 due to de`clare parents issue -->
<dependencies>
<!-- NB: You must use Maven 2.0.9 or above or these are ignored (see MNG-2972) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
使用JTA事务时,会话(即entityManager)会自动绑定到事务。这意味着在事务T1期间获取的实体是在与T1绑定的entityManager/会话中获取的。
一旦提交了T1,实体管理器/会话就不再附加到任何事务(因为T1已经完成)。
当您的客户执行此操作时:
Parent parent = service.findById( id );
Set<Child> children = service.getChildrenFor( parent );
parent
是在T1期间获取的,因此它绑定到与T1绑定的entityManager
(我们称之为EM1)。但是T1已经完成(它是在findById
return时提交的)。
因为getChildrenFor
是用@Transactional
注释的:一个新的tx(即.T2)由txManager启动。这将创建一个新的entityManager(即。EM2)与T2相关联。但是父
属于EM1,并且EM1仍然没有绑定到任何正在运行的tx。
要解决您的问题,您可以调整此方法的代码:
@Transactional
public Set<Child> getChildrenFor( Parent parent ) {
Parent mergedParent = entityManager.merge(parent);
return Collections.unmodifiableSet( new HashSet<>( mergedParent.getChildren() ) );
}
调用合并
将
将给定实体的状态合并到当前持久性上下文中。
(请注意,持久性上下文是与当前entityManager关联的存储)
mergedParent
现在属于EM2,并且EM2绑定到当前运行的T2,因此调用mergedParent。getChildren()
不应该失败。
关于merge
的重要备注:需要注意的是merge
返回一个新实例,并且不要接触传入参数的实例。在使用JPA时,认为merge
修改实例是一个非常常见的错误/误解。
在这一点上,我希望您理解,当您在同一个tx中获取父级和子级时(调用getChildrenFor(Long id)
),没有必要合并,因为父级和子级都属于同一个entityManager。
长话短说:我们开发并维护了一个库,可以在使用JavaEE7/CDI/JPA的其他项目中使用。应用程序将在Glassfish-4.0下运行,并使用Hibernate的JPA实现实现底层的PostgreSQL持久性。这是将用Spring/Struts/Hibernate编写的旧应用程序重写到JavaEE7/CDI/JTA新世界的长期迁移工作的一部分。 问题:出于审计目的,我们的库需要在执行用户语句之前
并用@Transactional注释了具体类。 我们使用Jboss应用服务器支持通过JNDI与MQ集成。这里的问题是,如果监听器中的任何层有任何异常,则整个事务不会回滚,消息也不会移动到退出队列。很明显,当我们使用Hibernate事务管理器时,它不知道其他资源,如JMS事务。 我可以安全地用JTA事务替换它吗,因为Jboss将处理整个事务管理?这样做是否有任何可预见的风险?
我指的是https://developer.jboss.org/wiki/SessionsAndTransactions,目前正试图了解与JTA的划界问题。它声明在使用getCurrentSession()的特定事务中,总是给出相同的当前会话。是不是意味着: < li >如果另一个用户在另一个线程中执行同一段代码(通过查找获取一个事务,然后使用getCurrentSession()并关闭该事务),
环境: 我们有一个应用程序部署在 JBoss 4.2.3.GA 服务器中,它使用Hibernate 3.4 和 JTA 1.0。 有一个导入器创建或更新某些实体,然后导入一些数据。由于多种原因,大部分导入是在新事务中完成的,在每个事务中,在外部事务中创建/更新的实体可能会再次更新。 调用序列类似于以下伪代码: 服务1: 服务2: 问题: 现在的问题是,我们最终会遇到一个竞争条件,有几个事务试图锁定
我对Hibernate中的会话和事务的概念有点困惑。据我所知,Hibernate使用会话(持久性上下文),它基本上是需要持久,删除或数据库中任何内容的实体的缓存。会话封装事务,因此我启动一个会话,然后创建一个事务。事务关闭后,持久性上下文中的所有内容都将刷新到数据库,如果我关闭会话,也会发生同样的事情。 为什么我需要两者?我可以在不创建交易的情况下做同样的事情吗?
我正在计划一个设计,我将从同一个池中获得两个连接(plocal)。仅在一个连接上启动事务,而不在另一个连接上启动事务。我希望在同一过程中使用这两个数据库连接,并使用非事务连接进行模式调用,使用事务连接进行支持事务的记录级调用。这种方法有效吗?