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() ) );
}