gpt4 book ai didi

java - 多个JTA事务: no session associated with the current transaction

转载 作者:行者123 更新时间:2023-12-01 14:53:19 25 4
gpt4 key购买 nike

我遇到一个问题,如果我有一个客户端在我的服务上调用两个方法,则会失败,第二个方法中的事务没有与之关联的 session 。但是,如果我将这两种方法组合到服务中并从客户端代码调用该方法,那么它就会成功。

谁能向我解释一下为什么会发生这种情况?

考虑以下代码:

@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 抛出一个异常,表示没有与当前事务关联的 session ,因此它无法迭代 #getChildren 的延迟加载的 PersistentSet。

现在我不是 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 事务时, session (即或多或少的实体管理器)会自动绑定(bind)到事务。这意味着事务 T1 期间获取的实体是在与 T1 绑定(bind)的实体管理器/ session 中获取的。

一旦提交 T1,entityManager/session 就不再附加到任何事务(因为 T1 已完成)。

当您的客户这样做时:

Parent parent = service.findById( id );
Set<Child> children = service.getChildrenFor( parent );

parent 在 T1 期间获取,因此它绑定(bind)到与 T1 绑定(bind)的 entityManager(我们称之为 EM1)。但 T1 已完成(当 findById 返回时已提交)。

由于 getChildrenFor 带有 @Transactional 注释:txManager 启动了一个新的 tx(即 T2)。这将创建一个与 T2 关联的新实体管理器(即 EM2)。但 parent 属于 EM1,并且 EM1 仍未绑定(bind)到任何正在运行的交易。

要解决您的问题,您可以调整此方法的代码:

@Transactional
public Set<Child> getChildrenFor( Parent parent ) {
Parent mergedParent = entityManager.merge(parent);
return Collections.unmodifiableSet( new HashSet<>( mergedParent.getChildren() ) );
}

调用merge将会

Merge the state of the given entity into the current persistence context.

(请注意,持久化上下文是与当前entityManager关联的存储)

mergedParent 现在属于 EM2,并且 EM2 绑定(bind)到当前运行的 T2,因此调用 mergedParent.getChildren() 不会失败。

关于merge的重要说明:重要的是要注意merge返回一个新实例并且不' t 触摸参数中传递的实例。在使用 JPA 时,认为合并修改实例是一个非常常见的错误/误解。

此时,我希望您明白,当您在同一个 tx 中获取父级和子级(调用 getChildrenFor( Long id ))时,无需合并,因为(父级和子级) Children) 属于同一个entityManager。

关于java - 多个JTA事务: no session associated with the current transaction,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14591287/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com