【问题标题】:working with two transactions, one seem to work but the other does not. why?处理两项事务,一项似乎有效,另一项无效。为什么?
【发布时间】:2014-01-29 20:27:47
【问题描述】:

我在 spring 上处理两个事务,它引用了两个实体管理器,因此引用了两个数据源,而使用 @Transactional("transaction1") 它工作正常,但 @Transactional("transaction2") 抛出一个错误,指出没有找到活动事务。以下是一段代码:

    @Bean(name = "transaction1")
        public PlatformTransactionManager transactionManager() {
            JpaTransactionManager transactionManager = new JpaTransactionManager();
            transactionManager.setEntityManagerFactory(entityManagerFactoryBean2()
                    .getObject());
            return transactionManager;
        }

    @Bean(name = "transaction2")
        public PlatformTransactionManager sermaTransactionManager() {

            JpaTransactionManager transactionManager = new JpaTransactionManager();
            transactionManager
                    .setEntityManagerFactory(entityManagerFactoryBean1()
                            .getObject());

            return transactionManager;
        }

各个实体经理:

    @Bean(name = "entitymanager1")
        public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean1() {

            LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
            entityManagerFactoryBean.setDataSource(dataSource1());
            entityManagerFactoryBean
                    .setPackagesToScan(new String[] { this.environment
                            .getProperty("db.packagesToScan") });
            entityManagerFactoryBean
                    .setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
            entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter());

            Map<String, Object> jpaProperties = new HashMap<String, Object>();
            jpaProperties.put("eclipselink.weaving", "false");
            jpaProperties.put("eclipselink.logging.level", "INFO");
            jpaProperties.put("eclipselink.logging.parameters", "true");
            entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);

            return entityManagerFactoryBean;
        }

    @Bean(name = "entitymanager2")
        public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean2() {

            LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
            entityManagerFactoryBean.setDataSource(dataSource2());
            entityManagerFactoryBean
                    .setPackagesToScan(new String[] { this.environment
                            .getProperty("db.packagesToScan") });
            entityManagerFactoryBean
                    .setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
            entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter());

            Map<String, Object> jpaProperties = new HashMap<String, Object>();
            jpaProperties.put("eclipselink.weaving", "false");
            jpaProperties.put("eclipselink.logging.parameters", "true");
            jpaProperties.put("eclipselink.logging.level", "INFO");
            entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);

            return entityManagerFactoryBean;
        }

各自的数据源:

@Bean("datasource1")
public DriverManagerDataSource dataSource1() {

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource                        .setDriverClassName(this.environment.getProperty("db.driver"));
        dataSource.setUrl(this.environment.getProperty("db.url"));
        dataSource.setUsername(this.environment
                .getProperty("db.username.abc"));
        dataSource.setPassword(this.environment
                .getProperty("db.password.abc"));

        return dataSource;
    }
@Bean("datasource2")
public DriverManagerDataSource dataSource2() {

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource
                .setDriverClassName(this.environment.getProperty("db.driver"));
        dataSource.setUrl(this.environment.getProperty("db.url"));
        dataSource.setUsername(this.environment.getProperty("db.username"));
        dataSource.setPassword(this.environment.getProperty("db.password"));

        return dataSource;
    }

我的 genericDaoImpl 具有以下链接各个实体管理器:

@PersistenceContext(unitName = "entitymanager1")
    private EntityManager em1;

    @PersistenceContext(unitName = "entitymanager2")
    private EntityManager em2;

当我执行 @Transactional("transaction1") 时,它工作正常,但是当我执行 @Transactional("transaction2") 时,它说没有活动事务。当我没有提到交易的限定符时,错误说找到了两个交易(transaction1,transaction2)。任何帮助都会很棒。

【问题讨论】:

  • 我相信您需要在 applicationContext.xml 中声明建议以传播到事务
  • transactionManager1 正在传递 entityManagerFactoryBean2,是否正确?
  • 您尝试过下面发布的配置吗?它正在工作
  • @jhadesdev 感谢您的回复,我按照您自动接线的方式进行操作,但我的两个交易似乎都附加了相同的数据源。当我在我的 daoimpl 上自动连接数据源时,两个不同的数据源有两个不同的用户/密码,但是当我为两个限定符执行 @Transactional("qualifierName") 时,它会获得相同的数据源。为什么会这样?我确保正确的数据源和实体管理器是自动装配的。
  • 使用 JpaTransactionManager,您在给定时间只能在服务层中拥有一个活动事务。请参阅下面的方法 savePlanes 的示例。只有使用 entityManager1 所做的更改才会与使用 @Transaction("transactionManager1") 注释的方法内的数据库同步。使用 entityManager2 所做的更改将被忽略(尽管仍会执行读取)。我测试了这个配置并且它有效,如果你总是得到相同的事务,也许你在 web.xml 中使用 OpenEntityManagerInView 过滤器,或者 OpenSessionInView

标签: java spring jpa jdbc entity


【解决方案1】:

配置有问题:

问题是当bean像这样初始化时:

transactionManager.setEntityManagerFactory(entityManagerFactoryBean2().getObject());

entityManagerFactoryBean2() 的调用将创建一个新的实体管理器工厂,然后@Bean 注解将触发创建另一个 实体管理器工厂,具有相同的配置。

当您在 bean 中使用 @Autowired 注入实体管理器工厂时,您正在注入使用 @Bean 创建的实例,而不是传递给事务管理器的实例。

关于配置的一个警告:

上面的配置允许在两个单独的数据源中执行事务,但是使用此配置不可能执行跨越 两个 数据库的事务。例如:

@Service
public class PlanesService {

    @PersistenceContext(unitName = "entityManagerFactory1")
    private EntityManager em1;

    @PersistenceContext(unitName = "entityManagerFactory2")
    private EntityManager em2;


    @Transactional("transactionManager1")
    public Plane savePlanes() {
        F14 f14 = new F14("F14","f14");
        F16 f16 = new F16("F16","f16");

        em1.persist(f14);
        em2.persist(f16);

        return f14;
    }
}

此代码将仅保留 f14,因为唯一正在进行事务的实体管理器是 em1(由于 @Transactional("transactionManager1"))。对em2.persist() 的调用将被忽略(尽管em2 仍然可以读取)。

如果您想要执行包含两个数据库的事务,则需要 JTA 事务管理器。

工作配置示例:

这是解决上述注入问题的配置示例:

@Configuration
public class DataSourcesConfig {

    @Bean(name = "datasource1")
    public DriverManagerDataSource dataSource1() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        ...
        return dataSource;
    }

    @Bean(name = "datasource2")
    public DriverManagerDataSource dataSource2() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        ...
        return dataSource;
    }
}

@Configuration
public class EntityManagerFactoriesConfiguration {

    @Autowired
    @Qualifier("datasource1")
    private DataSource dataSource1;

    @Autowired
    @Qualifier("datasource2")
    private DataSource dataSource2;


    @Bean(name = "entityManagerFactory1")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource1);
        entityManagerFactoryBean.setPackagesToScan(new String[] { "your.package.here" });
        entityManagerFactoryBean.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        Map<String, Object> jpaProperties = new HashMap<String, Object>();
        ...
        entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);

        return entityManagerFactoryBean;
    }

    @Bean(name = "entityManagerFactory2")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean2() {

        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource2);
        entityManagerFactoryBean.setPackagesToScan(new String[] { "your.package.here" });
        entityManagerFactoryBean.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        Map<String, Object> jpaProperties = new HashMap<String, Object>();

        entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
        ...
        return entityManagerFactoryBean;
    }
}     


@Configuration
@EnableTransactionManagement
public class TransactionManagersConfig {

    @Autowired
    @Qualifier("entityManagerFactory1")
    EntityManagerFactory entityManagerFactory1;

    @Autowired
    @Qualifier("entityManagerFactory2")
    EntityManagerFactory entityManagerFactory2;

    @Autowired
    @Qualifier("datasource1")
    private DataSource dataSource1;

    @Autowired
    @Qualifier("datasource2")
    private DataSource dataSource2;

    @Bean(name = "transactionManager1")
    public PlatformTransactionManager transactionManager1() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory1);
        transactionManager.setDataSource(dataSource1);
        return transactionManager;
    }

    @Bean(name = "transactionManager2")
    public PlatformTransactionManager transactionManager2() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory2);
        transactionManager.setDataSource(dataSource2);
        return transactionManager;
    }
}

通过将配置拆分为多个 @Configuration 类并自动装配它们,我们确保始终使用相同的 bean(单例)。

注意@EnableTransactionManagement 注释,它启用@Transactional。

【讨论】:

  • 回复@jhadesdev 有点晚了,但我最终为我的应用程序使用了路由数据源。谢谢你,你的解释很好,我学到了很多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-11
  • 2020-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-05
相关资源
最近更新 更多