当JMS使用JPA / Hibernate更新记录时 - TransactionRequiredException:No Transaction正在进行中

时间:2018-03-06 16:57:25

标签: spring-boot spring-data spring-transactions spring-jms

我们正在做什么:更新已收到商户ID的订阅。

我们在activeMQ队列上收到商家ID。

JMSListener选择了商家ID,找到所收到商家的所有相关订阅。

处理所有订阅,确定谁需要更新(可以是零或多于一个)。

对于每个订阅,我们称之为服务方法udate(SubscriptionDTO subscriptionDTO)。查找订阅实体对象。此更新方法使用dto对象更新实体对象。然后我们调用flush方法。注意:Update方法使用@transactional注释。从service @ udpate(dto)方法调用flush方法。

我们在Windows和Linux中观察到了不同的行为。在Windows上它运行良好但在Linux上我们在执行flush方法后失败 - javax.persistence.TransactionRequiredException:没有事务正在进行中

我们尝试在JMSListner上添加@Transactional但在Linux上遇到同样的错误。但是当我们尝试没有调用flush方法错误时,但没有更新发生。订阅数据与之前相同。

不确定上面解释了多么好的问题。

寻求指导,非常感谢您的回复。

JPA配置

package edu.learn.config;

@Configuration
@EnableJpaRepositories(basePackages = "edu.learn.repositories",     entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef =     "transactionManager")
@EnableTransactionManagement
public class JpaConfiguration
{
private final String DEFAULT_TEST_QUERY = "SELECT 1";

@Autowired
private Environment environment;

@Value("${datasource.maxPoolSize:10}")
private int maxPoolSize;

/*
 * Populate SpringBoot DataSourceProperties object directly from
 * application.yml based on prefix.Thanks to .yml, Hierachical data is
 * mapped out of the box with matching-name properties of
 * DataSourceProperties object].
 */
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource")
public DataSourceProperties dataSourceProperties()
{
    return new DataSourceProperties();
}

/*
 * Configure HikariCP pooled DataSource.
 */
@Bean
public DataSource dataSource()
{
    DataSourceProperties dataSourceProperties = dataSourceProperties();

    HikariDataSource dataSource = (HikariDataSource) DataSourceBuilder.create()
            .type(com.zaxxer.hikari.HikariDataSource.class).username(dataSourceProperties.getUsername())
            .password(dataSourceProperties.getPassword()).build();
    dataSource.setMaximumPoolSize(PropertyUtil.getMaxPoolSize(environment));
    dataSource.setInitializationFailFast(PropertyUtil.getInitializationFailFast(environment));
    dataSource.setMinimumIdle(PropertyUtil.getMinIdle(environment));
    dataSource.setConnectionTestQuery(DEFAULT_TEST_QUERY);
    dataSource.setConnectionTimeout(PropertyUtil.getConnectionTimeout(environment));
    dataSource.setIdleTimeout(PropertyUtil.getIdleTimeout(environment));
    dataSource.setMaxLifetime(PropertyUtil.getMaxLifetime(environment));
    dataSource.setDataSourceClassName(PropertyUtil.getDataSourceClassName(environment));

    if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(PropertyUtil.getDataSourceClassName(environment)))
    {
        dataSource.addDataSourceProperty("databaseName", PropertyUtil.defaultSchema(environment));
        dataSource.addDataSourceProperty("cachePrepStmts", PropertyUtil.getCachePrepStmts(environment));
        dataSource.addDataSourceProperty("prepStmtCacheSize", PropertyUtil.getPrepStmtCacheSize(environment));
        dataSource.addDataSourceProperty(
                "prepStmtCacheSqlLimit", PropertyUtil.getPrepStmtCacheSqlLimit(environment)
        );
        dataSource.addDataSourceProperty("useServerPrepStmts", PropertyUtil.getUseServerPrepStmts(environment));
        dataSource.addDataSourceProperty("serverName", PropertyUtil.dbServerName(environment));
        dataSource.addDataSourceProperty("portNumber", PropertyUtil.portNumber(environment));
    }
    return dataSource;
}

/*
 * Entity Manager Factory setup.
 */
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException
{
    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setPackagesToScan(new String[]
    {
            "edu.learn.model"
    });
    factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
    factoryBean.setJpaProperties(jpaProperties());
    return factoryBean;
}

/*
 * Provider specific adapter.
 */
@Bean
public JpaVendorAdapter jpaVendorAdapter()
{
    HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
    return hibernateJpaVendorAdapter;
}

/*
 * Here you can specify any provider specific properties.
 */
private Properties jpaProperties()
{
    Properties properties = new Properties();
    properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
    properties.put("hibernate.globally_quoted_identifiers", "true");
    properties.put("hibernate.hbm2ddl.auto", "validates");
    properties.put("ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
    properties.put("show_sql", "false");
    properties.put("format_sql", "true");

    return properties;
}

@Bean
@Autowired
public PlatformTransactionManager transactionManager(EntityManagerFactory emf)
{
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(emf);
    return txManager;
}

static class PropertyUtil
{
    final static String DEFAULT_CHARACTER_ENCODING = "utf-8";
    final static String DEFAULT_DATASOURCE_CLASS = "com.mysql.jdbc.Driver";

    public static boolean getCachePrepStmts(final Environment environment)
    {
        String cachePrepStmts = environment.getProperty("spring.datasource.hikari.cachePrepStmts");
        if ("false".equalsIgnoreCase(cachePrepStmts))
        {
            return false;
        }
        return true;
    }

    public static int getPrepStmtCacheSize(final Environment environment)
    {
        String prepStmtCacheSize = environment.getProperty("spring.datasource.hikari.prepStmtCacheSize");
        try
        {
            return Integer.parseInt(prepStmtCacheSize);
        }
        catch(Exception e)
        {
            return 250;
        }
    }

    public static int getPrepStmtCacheSqlLimit(final Environment environment)
    {
        String prepStmtCacheSqlLimit = environment.getProperty("spring.datasource.hikari.prepStmtCacheSqlLimit");
        try
        {
            return Integer.parseInt(prepStmtCacheSqlLimit);
        }
        catch(Exception e)
        {
            return 2048;
        }
    }

    public static boolean getUseServerPrepStmts(final Environment environment)
    {
        String useServerPrepStmts = environment.getProperty("spring.datasource.hikari.useServerPrepStmts");
        if ("false".equalsIgnoreCase(useServerPrepStmts))
        {
            return false;
        }
        return true;
    }

    public static boolean getInitializationFailFast(final Environment environment)
    {
        String initializationFailFast = environment.getProperty("spring.datasource.hikari.initializationFailFast");
        if ("false".equalsIgnoreCase(initializationFailFast))
        {
            return false;
        }
        return true;
    }

    public static long getConnectionTimeout(final Environment environment)
    {
        String connectionTimeout = environment.getProperty("spring.datasource.hikari.connectionTimeout");
        try
        {
            return Integer.parseInt(connectionTimeout);
        }
        catch(Exception e)
        {
            return TimeUnit.SECONDS.toMillis(60);
        }
    }

    public static long getIdleTimeout(final Environment environment)
    {
        String idleTimeout = environment.getProperty("spring.datasource.hikari.idleTimeout");
        try
        {
            return Integer.parseInt(idleTimeout);
        }
        catch(Exception e)
        {
            return TimeUnit.SECONDS.toMillis(60);
        }
    }

    public static long getMaxLifetime(final Environment environment)
    {
        String maxLifetime = environment.getProperty("spring.datasource.hikari.maxLifetime");
        try
        {
            return Integer.parseInt(maxLifetime);
        }
        catch(Exception e)
        {
            return TimeUnit.SECONDS.toMillis(90);
        }
    }

    public static int getMinIdle(final Environment environment)
    {
        String minIdle = environment.getProperty("spring.datasource.hikari.minIdle");
        try
        {
            return Integer.parseInt(minIdle);
        }
        catch(Exception e)
        {
            return 5;
        }
    }

    public static int getMaxPoolSize(final Environment environment)
    {
        String maxPoolSize = environment.getProperty("spring.datasource.maxPoolSize");
        try
        {
            return Integer.parseInt(maxPoolSize);
        }
        catch(Exception e)
        {
            return 25;
        }
    }

    public static String getDataSourceClassName(final Environment environment)
    {
        String dataSourceClassName = environment.getProperty("spring.datasource.dataSourceClassName");
        if (dataSourceClassName != null && "".equalsIgnoreCase(dataSourceClassName.trim()) == false)
        {
            return dataSourceClassName;
        }
        return DEFAULT_DATASOURCE_CLASS;
    }

    public static String getCharacterEncoding(final Environment environment)
    {
        String characterEncoding = environment.getProperty("spring.datasource.characterEncoding");
        if (characterEncoding != null && "".equalsIgnoreCase(characterEncoding.trim()) == false)
        {
            return characterEncoding;
        }
        return DEFAULT_CHARACTER_ENCODING;
    }

    public static boolean getUniCode(final Environment environment)
    {
        String useUnicode = environment.getProperty("spring.datasource.useUnicode");
        if ("false".equalsIgnoreCase(useUnicode))
        {
            return false;
        }
        return true;
    }

    public static String showSQL(final Environment environment)
    {
        String showSQL = environment.getProperty("spring.datasource.hibernate.showSQL");
        if ("false".equalsIgnoreCase(showSQL))
        {
            return "false";
        }
        return "true";
    }

    public static String formatSQL(final Environment environment)
    {
        String formatSQL = environment.getProperty("spring.datasource.hibernate.format_sql");
        if ("false".equalsIgnoreCase(formatSQL))
        {
            return "false";
        }
        return "true";
    }

    public static String dbServerName(final Environment environment)
    {
        String dbServerName = environment.getProperty("spring.datasource.serverName");
        if (dbServerName == null || "".equalsIgnoreCase(dbServerName.trim()))
        {
            return "localhost";
        }
        return dbServerName;
    }

    public static int portNumber(final Environment environment)
    {
        String portNumber = environment.getProperty("spring.datasource.portNumber");
        try
        {
            return Integer.parseInt(portNumber);
        }
        catch(Exception e)
        {
            return 3306;
        }
    }

    public static String defaultSchema(final Environment environment)
    {
        String defaultSchema = environment.getProperty("spring.datasource.defaultSchema");
        if (defaultSchema == null || "".equalsIgnoreCase(defaultSchema.trim()))
        {
            return "subscription";
        }
        return defaultSchema;
    }
}
}

JMS配置

@EnableJms
@Configuration
public class JmsConfiguration
{
    @Bean
    public JmsListenerContainerFactory<?> myFactory(ConnectionFactory     connectionFactory,
                                                    DefaultJmsListenerContainerFactoryConfigurer configurer)
{
    DefaultJmsListenerContainerFactory factory = new     DefaultJmsListenerContainerFactory();
    configurer.configure(factory, connectionFactory);
    return factory;
}
}

启动课程

@Import({JpaConfiguration.class, JmsConfiguration.class})
@SpringBootApplication(scanBasePackages={"edu.learn"})
public class SubscriptionApiApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(SubscriptionApiApplication.class, args);
    }
}

JMS队列侦听器

@Component
public class OrderTransactionReceiver
{
    private static final Logger             LOGGER =     LoggerFactory.getLogger(OrderTransactionReceiver.class);

    @Autowired
    private SubscriptionService             subsriptionService;

    @JmsListener(destination = "OrderTransactionQueue", containerFactory = "myFactory")
public void receiveMessage(String businessID)
{
    List<SubscriptionDTO> subscriptions = subsriptionService.findByBusinessID(businessID);
    for (SubscriptionDTO subscription : subscriptions)
    {
        subscription.setStatus("active");
        subsriptionService.update(subscription);
    }
}
}

服务

@Service
class RepositorySubscriptionService implements SubscriptionService
{
private static final Logger LOGGER = LoggerFactory.getLogger(RepositorySubscriptionService.class);

private final SubscriptionRepository          repository;

@Transactional
@Override
public SubscriptionDTO update(SubscriptionDTO updatedSubscriptionEntry)
{
    LOGGER.info(
            "Updating the information of a subscription entry by using information: {}", updatedSubscriptionEntry
    );

    SubscriptionEntity updated = findSubscriptionEntryById(updatedSubscriptionEntry.getId());

    updated.update(updatedSubscriptionEntry.getStatus());

    // We need to flush the changes or otherwise the returned object
    // doesn't contain the updated audit information.
    repository.flush();

    LOGGER.info("Updated the information of the subscription entry: {}", updated);

    return SubscriptionMapper.mapEntityIntoDTO(updated);
}
}

Repostiory

public interface SubscriptionRepository extends 
    Repository<SubscriptionEntity, String>, 
    JpaSpecificationExecutor<SubscriptionEntity>
{
void delete(SubscriptionEntity deleted);

List<SubscriptionEntity> findAll();

Optional<SubscriptionEntity> findOne(String id);

List<SubscriptionEntity> findByBusinessID(final String businessID);

void flush();

SubscriptionEntity save(SubscriptionEntity persisted);
}

JTA交易配置

@Configuration
@EnableJpaRepositories(basePackages = "edu.learn.repositories", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
@EnableTransactionManagement
public class JpaConfiguration
{
private final String DEFAULT_TEST_QUERY = "SELECT 1";

@Autowired
private Environment environment;

@Value("${datasource.maxPoolSize:10}")
private int maxPoolSize;

/*
 * Populate SpringBoot DataSourceProperties object directly from
 * application.yml based on prefix.Thanks to .yml, Hierachical data is
 * mapped out of the box with matching-name properties of
 * DataSourceProperties object].
 */
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource")
public DataSourceProperties dataSourceProperties()
{
    return new DataSourceProperties();
}

/*
 * Configure HikariCP pooled DataSource.
 */
@Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSource()
{
    DataSourceProperties dataSourceProperties = dataSourceProperties();

    MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
    mysqlXaDataSource.setServerName(PropertyUtil.dbServerName(environment));
    mysqlXaDataSource.setPort(PropertyUtil.portNumber(environment));
    mysqlXaDataSource.setUser(dataSourceProperties.getUsername());
    mysqlXaDataSource.setPassword(dataSourceProperties.getPassword());
    mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
    mysqlXaDataSource.setDatabaseName(PropertyUtil.defaultSchema(environment));
    mysqlXaDataSource.setCachePreparedStatements(PropertyUtil.getCachePrepStmts(environment));
    try
    {
        mysqlXaDataSource.setPrepStmtCacheSqlLimit(PropertyUtil.getPrepStmtCacheSqlLimit(environment));
        mysqlXaDataSource.setPrepStmtCacheSize(PropertyUtil.getPrepStmtCacheSize(environment));
    }
    catch (SQLException e)
    {
        e.printStackTrace();
    }

    AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
    xaDataSource.setXaDataSource(mysqlXaDataSource);
    xaDataSource.setUniqueResourceName("xaSubscription");
    xaDataSource.setTestQuery(DEFAULT_TEST_QUERY);
    xaDataSource.setMaxPoolSize(PropertyUtil.getMaxPoolSize(environment));
    xaDataSource.setMaxIdleTime(PropertyUtil.getMinIdle(environment));
    xaDataSource.setMaxLifetime((int)PropertyUtil.getMaxLifetime(environment));
    xaDataSource.setBorrowConnectionTimeout((int)PropertyUtil.getConnectionTimeout(environment));

    return xaDataSource;
}

/*
 * Entity Manager Factory setup.
 */
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException
{
    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setPackagesToScan(new String[]
    {
            "edu.learn.model"
    });
    factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
    factoryBean.setJpaProperties(jpaProperties());
    return factoryBean;
}

/*
 * Provider specific adapter.
 */
@Bean
public JpaVendorAdapter jpaVendorAdapter()
{
    HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
    return hibernateJpaVendorAdapter;
}

/*
 * Here you can specify any provider specific properties.
 */
private Properties jpaProperties()
{
    Properties properties = new Properties();
    properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
    properties.put("hibernate.globally_quoted_identifiers", "true");
    properties.put("hibernate.hbm2ddl.auto", "validates");
    properties.put("ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
    properties.put("show_sql", "false");
    properties.put("format_sql", "true");
    properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
    properties.put("javax.persistence.transactionType", "JTA");

    return properties;
}


@Bean
public UserTransaction userTransaction() throws Throwable
{
    UserTransactionImp userTransactionImp = new UserTransactionImp();
    userTransactionImp.setTransactionTimeout(1000);
    return userTransactionImp;
}

@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable
{
    UserTransactionManager userTransactionManager = new UserTransactionManager();
    userTransactionManager.setForceShutdown(false);

    AtomikosJtaPlatform.transactionManager = userTransactionManager;

    return userTransactionManager;
}

@Bean(name = "transactionManager")
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
public PlatformTransactionManager transactionManager() throws Throwable
{
    UserTransaction userTransaction = userTransaction();

    AtomikosJtaPlatform.transaction = userTransaction;

    TransactionManager atomikosTransactionManager = atomikosTransactionManager();
    return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}

public class AtomikosJtaPlatform extends AbstractJtaPlatform
{

private static final long serialVersionUID = 1L;

static TransactionManager transactionManager;
static UserTransaction    transaction;

@Override
protected TransactionManager locateTransactionManager()
{
    return transactionManager;
}

@Override
protected UserTransaction locateUserTransaction()
{
    return transaction;
}
}

0 个答案:

没有答案