Java 多个JTA事务:没有与当前事务关联的会话
我有一个问题,如果我的服务上有一个客户端调用两个方法,它就会失败,因为第二个方法中的事务没有与之关联的会话。但是,如果我将这两种方法组合到服务中,并从客户机代码中调用其中一种方法,它就会成功 有人能解释一下为什么会发生这种情况吗 考虑以下代码:Java 多个JTA事务:没有与当前事务关联的会话,java,spring,hibernate,jpa,Java,Spring,Hibernate,Jpa,我有一个问题,如果我的服务上有一个客户端调用两个方法,它就会失败,因为第二个方法中的事务没有与之关联的会话。但是,如果我将这两种方法组合到服务中,并从客户机代码中调用其中一种方法,它就会成功 有人能解释一下为什么会发生这种情况吗 考虑以下代码: @Configurable public class ParentService { @PersistenceContext private EntityManager entityManager; public Parent
@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 );
}
...
}
请让我知道我可以提供的任何其他信息,以帮助解决此问题
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>
@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:
org.codehaus.mojo
aspectj maven插件
1.2
org.aspectj
aspectjrt
${aspectj.version}
org.aspectj
aspectjtools
${aspectj.version}
编译
测试编译
真的
org.springframework
春季方面
${java.version}
${java.version}
处理JTA事务时,会话(即或多或少的entityManager)会自动绑定到事务。
这意味着在事务T1期间获取的实体是在与T1绑定的entityManager/会话中获取的
一旦提交了T1,entityManager/会话就不再附加到任何事务(因为T1已经完成)
当您的客户执行此操作时:
Parent parent = service.findById( id );
Set<Child> children = service.getChildrenFor( parent );
召唤遗嘱
将给定实体的状态合并到当前持久性上下文中
(请注意,持久性上下文是与当前entityManager关联的存储)
mergedParent
现在属于EM2,并且EM2绑定到当前运行的T2,因此调用mergedParent.getchildrent()
应该不会失败
重要备注关于合并
:请务必注意,合并
返回一个新实例,并且不要触摸传入参数的实例。在使用JPA时,认为merge
修改实例是一个非常常见的错误/误解
在这一点上,我希望您理解,当您在同一个tx(调用
getChildrenFor(Long id)
)中获取父级和子级时,没有必要合并,因为两者(父级和子级)属于同一个entityManager。您真的需要围绕读取操作的事务吗?我不这么认为,但显然我需要。如果我删除了所有事务性注释,那么客户端将失败,并出现LazyInitializationException异常,即使在#getChildrenOf(id)的聚合方法中也是如此,而在此之前,至少有一个成功。您可以发布您的hibernate配置吗?@ben75,请告诉我这是否提供了任何细节。谢谢文章中有些混乱:你有时使用getChildrenFor
,有时使用getChildrenOf
(根据代码正确的是getChildrenFor
)ben75,完美的解释。我假设我没有得到像分离引用这样的其他异常,因为#getChildren是实体上的一个实例方法。有没有一种方法可以为我的应用程序(假设它不是一个webapp)创建一个全局会话,而不必将客户端应用程序的方法标记为@Transactional?再次感谢。这是可能有长期生活会话(即使在一个网络应用程序)。对“扩展持久性上下文”做一些研究。其思想是在需要执行数据库访问时加入正在运行的事务。这不是一个“容易的话题”,所以我建议你研究一下这个领域,如果需要的话,带着其他问题回来。
@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;
}
}
<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>
Parent parent = service.findById( id );
Set<Child> children = service.getChildrenFor( parent );
@Transactional
public Set<Child> getChildrenFor( Parent parent ) {
Parent mergedParent = entityManager.merge(parent);
return Collections.unmodifiableSet( new HashSet<>( mergedParent.getChildren() ) );
}