注意: 鉴于问题的性质和复杂性,我努力在描述问题和提供上下文之间取得平衡。可能,但仍然简洁。请说明需要在何处提供任何其他信息或说明。
目前,我有一个简单的SQL UPDATE(如下所示)抛出TransactionRequiredException。必须注意的是,所有finder查询都成功执行,并将适当的结果集返回给服务层,而更新查询则失败并出现错误。
javax.persistence.TransactionRequiredException: Executing an update/delete query
我在存储库类中定义了一个简单的SQL查询,如下所示:
public interface SlaQualifyingItemsRepository extends JpaRepository<SlaQualifyingItemsEntity, RowId>
{
... ... ...
... ... ...
@Modifying
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=RuntimeException.class)
@Query("UPDATE SlaQualifyingItemsEntity sqie SET sqie.affectedCiId = :affectedCiId WHERE sqie.id.slice=:slice AND sqie.id.rowId IN (:rowIds)")
public void updateInBulkAffectedService(Integer slice, List<Long> rowIds, Long affectedCiId);
... ... ...
... ... ...
}
以下仅是项目的pom.xml中配置的关键组件版本。
以下是与项目相关的其他一些相关信息:
txManager.setAutodetectDataSource(假);
我尝试解决此问题的各种不同的事情,但我始终遇到同样的问题。
@Override
protected Object doExecute(AbstractJpaQuery query, Object[] values) {
int result = query.createQuery(values).executeUpdate();
... ... ...
... ... ...
public int executeUpdate() throws HibernateException {
if ( ! getProducer().isTransactionInProgress() ) {
throw getProducer().getExceptionConverter().convert(
new TransactionRequiredException(
"Executing an update/delete query"
)
);
}
public abstract class DatabaseConfig
{
private static final Logger logger = LogManager.getLogger(DatabaseConfig.class);
@Autowired
private Environment environment;
@Autowired
private MapDataSourceLookup dataSourceLookup;
@Bean(name="platformEntityManagerFactory")
@Qualifier("platformEntityManagerFactory")
public EntityManagerFactory platformEntityManagerFactory()
{
logger.trace("Initializing CSM platform entity manager configurations.");
// Exposes JpaVendorAdapter implementation for Hibernate's Persistence Provider and EntityManager interface
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabasePlatform(environment.getProperty(org.hibernate.cfg.Environment.DIALECT));
jpaVendorAdapter.setShowSql(Boolean.getBoolean(environment.getProperty(org.hibernate.cfg.Environment.SHOW_SQL)));
// Set up a shared JPA EntityManagerFactory in a Spring application context; the EntityManagerFactory can then be passed to
// JPA-based DAOs via dependency injection
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
// Trigger Spring to scan for classes annotated with @Entity and @MappedSuperclass and automatically add those to the JPA
// PersistenceUnit
factory.setPackagesToScan("com.sa.csm.dao.mta.model");
// Specify the JpaVendorAdapter implementation for the desired JPA provider
factory.setJpaVendorAdapter(jpaVendorAdapter);
// Sets up an embedded data source using Spring’s embedded database support.
factory.setDataSource(platformDataSource());
factory.setJpaProperties(jpaProperties());
// Perform initialization after all the properties are set; a misconfiguration will result in an exception being thrown
factory.afterPropertiesSet();
return factory.getObject();
}
/**
* <p>External configuration properties for a JPA EntityManagerFactory created by Spring.</p>
*
* @return A map containing JPA configuration properties
*/
private Properties jpaProperties()
{
// Configure Hibernate-based JPA Settings when initializing database sessions
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.STATEMENT_BATCH_SIZE,
environment.getProperty(org.hibernate.cfg.Environment.STATEMENT_BATCH_SIZE));
jpaProperties.put(org.hibernate.cfg.Environment.STATEMENT_FETCH_SIZE,
environment.getProperty(org.hibernate.cfg.Environment.STATEMENT_FETCH_SIZE));
jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL,
environment.getProperty(org.hibernate.cfg.Environment.SHOW_SQL));
jpaProperties.put(org.hibernate.cfg.Environment.FORMAT_SQL,
environment.getProperty(org.hibernate.cfg.Environment.FORMAT_SQL));
jpaProperties.put(org.hibernate.cfg.Environment.MAX_FETCH_DEPTH,
environment.getProperty(org.hibernate.cfg.Environment.MAX_FETCH_DEPTH));
jpaProperties.put(org.hibernate.cfg.Environment.AUTOCOMMIT,
environment.getProperty(org.hibernate.cfg.Environment.AUTOCOMMIT));
jpaProperties.put(org.hibernate.cfg.Environment.USE_SECOND_LEVEL_CACHE,
environment.getProperty(org.hibernate.cfg.Environment.USE_SECOND_LEVEL_CACHE));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, "validate");
return jpaProperties;
}
@Bean(name="platformDataSource")
@Qualifier("platformDataSource")
public DataSource platformDataSource()
{
logger.trace("Initializing CSM platform datasource configurations.");
DataSource platformDataSource = new DataSource();
platformDataSource.setDefaultAutoCommit(true);
platformDataSource.setDefaultReadOnly(false);
platformDataSource.setDefaultCatalog(environment.getProperty("mta.db.catalog"));
platformDataSource.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
platformDataSource.setDriverClassName(environment.getProperty("mta.db.driver"));
platformDataSource.setUrl(environment.getProperty("mta.db.url"));
platformDataSource.setUsername(environment.getProperty("mta.db.user"));
platformDataSource.setPassword(DAOUtils.decrypt(environment.getProperty("mta.db.password")));
return platformDataSource;
}
@Bean(name="csmDataSources")
@Qualifier("csmDataSources")
public Map<String, DataSource> dataSourcesCsm()
{
logger.trace("Initializing CSM Platform Datasource.");
return ((MultiTenantDataSourceLookup)dataSourceLookup).initializeDataSources(platformDataSource());
}
@Primary
@Bean(name="platformTransactionManager")
@Qualifier("platformTransactionManager")
public PlatformTransactionManager transactionManager()
{
logger.trace("Initializing CSM platform transaction manager.");
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(platformEntityManagerFactory());
return transactionManager;
}
@Bean(name="platformSessionFactory")
@Qualifier("platformSessionFactory")
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(platformDataSource());
sessionFactory.setPackagesToScan(new String[] { "com.sa.csm.dao.mta.model" });
sessionFactory.setHibernateProperties(jpaProperties());
return sessionFactory;
}
@Bean(name="hibernateExceptionTranslator")
@Qualifier("hibernateExceptionTranslator")
public HibernateExceptionTranslator hibernateExceptionTranslator()
{
logger.trace("Initializing hibernate exception translator.");
return new HibernateExceptionTranslator();
}
@Bean(name="poolingStrategy")
@Qualifier("poolingStrategy")
public PoolingStrategy poolingStrategy()
{
return PoolingStrategy.getPoolingStrategyByCode(environment.getProperty("mta.db.pooling.strategy"));
}
}
/**
* <p>
* This is the base class that implements configuration of multitenancy with multiple databases and API services using Spring JPA and
* Hibernate. All the multi-tenant JPA related beans are instantiated from here. During the bootstrapping process, it configures the general
* JPA infrastructure ((i.e., a DataSource connecting to a database as well as a JPA EntityManagerFactory) for the CSM tenant database
* access.
* </p>
*
* @author thada02
*
*/
public abstract class MultiTenantDatabaseConfig
{
private static final Logger logger = LogManager.getLogger(MultiTenantDatabaseConfig.class);
@Inject
private Environment environment;
@Bean(name="entityManagerFactoryBean")
@Qualifier("entityManagerFactoryBean")
@DependsOn(value={"multiTenantConnectionProvider","currentTenantIdentifierResolver"})
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
MultiTenantConnectionProvider multitenantConnectionProvider,
CurrentTenantIdentifierResolver currentTenantIdentifierResolver)
{
logger.trace("Initializing entity manager factory bean for enabling multi-tenancy configuration.");
// Exposes JpaVendorAdapter implementation for Hibernate's Persistence Provider and EntityManager interface
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabasePlatform(environment.getProperty(org.hibernate.cfg.Environment.DIALECT));
jpaVendorAdapter.setShowSql(Boolean.getBoolean(environment.getProperty(org.hibernate.cfg.Environment.SHOW_SQL)));
// Configure Hibernate-based JPA Settings when initializing database sessions
Properties jpaProperties = this.jpaProperties();
jpaProperties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER, multitenantConnectionProvider);
jpaProperties.put(org.hibernate.cfg.Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
// No dataSource is set to resulting entityManagerFactoryBean
LocalContainerEntityManagerFactoryBean result = new LocalContainerEntityManagerFactoryBean();
// Trigger Spring to scan for classes annotated with @Entity and @MappedSuperclass and automatically add those to the JPA
// PersistenceUnit
String[] packagesToScan = environment.getProperty("jpa.entity.packages.to.scan").split(",");
result.setPackagesToScan(packagesToScan);
// Specify the JpaVendorAdapter implementation for the desired JPA provider
result.setJpaVendorAdapter(jpaVendorAdapter);
result.setJpaProperties(jpaProperties);
// Perform initialization after all the properties are set; a misconfiguration will result in an exception being thrown
result.afterPropertiesSet();
return result;
}
/**
* <p>External configuration properties for a JPA EntityManagerFactory created by Spring.</p>
*
* @return A map containing JPA configuration properties
*/
private Properties jpaProperties()
{
// Configure Hibernate-based JPA Settings when initializing database sessions
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, environment.getProperty(org.hibernate.cfg.Environment.DIALECT));
jpaProperties.put(org.hibernate.cfg.Environment.STATEMENT_BATCH_SIZE, environment.getProperty(org.hibernate.cfg.Environment.STATEMENT_BATCH_SIZE));
jpaProperties.put(org.hibernate.cfg.Environment.STATEMENT_FETCH_SIZE, environment.getProperty(org.hibernate.cfg.Environment.STATEMENT_FETCH_SIZE));
jpaProperties.put(org.hibernate.cfg.Environment.DEFAULT_SCHEMA, environment.getProperty(org.hibernate.cfg.Environment.DEFAULT_SCHEMA));
jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, environment.getProperty(org.hibernate.cfg.Environment.SHOW_SQL));
jpaProperties.put(org.hibernate.cfg.Environment.FORMAT_SQL, environment.getProperty(org.hibernate.cfg.Environment.FORMAT_SQL));
jpaProperties.put(org.hibernate.cfg.Environment.MAX_FETCH_DEPTH, environment.getProperty(org.hibernate.cfg.Environment.MAX_FETCH_DEPTH));
jpaProperties.put(org.hibernate.cfg.Environment.AUTOCOMMIT, environment.getProperty(org.hibernate.cfg.Environment.AUTOCOMMIT));
jpaProperties.put(org.hibernate.cfg.Environment.USE_SECOND_LEVEL_CACHE, environment.getProperty(org.hibernate.cfg.Environment.USE_SECOND_LEVEL_CACHE));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, "none");
jpaProperties.put(org.hibernate.cfg.Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE.name());
return jpaProperties;
}
/**
* <p>
* Obtain an instance of the JPA Entity Manager Factory for obtaining the hibernate's session factory.
* </p>
*
* @param entityManagerFactoryBean The entity manager factory bean
* @return An entity manager factory
*/
@Bean(name="entityManagerFactory")
@Qualifier("entityManagerFactory")
@Autowired
public EntityManagerFactory entityManagerFactory(@Qualifier("entityManagerFactoryBean") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean)
{
logger.trace("Initializing entity manager factory associated for multi-tenancy configuration.");
return entityManagerFactoryBean.getObject();
}
/**
* <p>
* Initialize the hibernate 5 transaction manager using the using the application-managed entity manager factory.
* </p>
*
* @param entityManagerFactory
* The entity manager
* @return The transaction manager
*/
@Bean(name="transactionManager")
@Qualifier("transactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory)
{
logger.trace("Initializing transaction manager for multi-tenancy configuration.");
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
HibernateTransactionManager txManager = new HibernateTransactionManager();
// The txManager bean needs to unwrap the EntityManagerFactory implementation, Hibernate's SessionFactory in this case, to set the
// AutodetectDataSource attribute to false, this is a requirement for multitenancy to work
txManager.setAutodetectDataSource(false);
txManager.setSessionFactory(sessionFactory);
return txManager;
}
/**
* <p>
* Returns an instance of <code>MultiTenantConnectionProvider</code>
* </p>
*
* @return An instance of MultiTenantConnectionProvider
*/
@Bean(name="multiTenantConnectionProvider")
@Qualifier("multiTenantConnectionProvider")
public MultiTenantConnectionProvider multiTenantConnectionProvider()
{
return new MultiTenantConnectionProviderImpl();
}
/**
* <p>
* Returns an instance of <code>CurrentTenantIdentifierResolver</code>
* </p>
*
* @return An instance of CurrentTenantIdentifierResolver
*/
@Bean(name="currentTenantIdentifierResolver")
@Qualifier("currentTenantIdentifierResolver")
public CurrentTenantIdentifierResolverImpl currentTenantIdentifierResolver()
{
return new CurrentTenantIdentifierResolverImpl();
}
}
@Service
public class SlaQualifyingItemsDataAccessService
{
private static final Logger logger = LogManager.getLogger(SlaQualifyingItemsDataAccessService.class);
@Autowired
private SlaQualifyingItemsRepository slaQualifyingItemsRepository;
... ... ...
... ... ...
/**
* <p>Update Affected Service for the SLA Qualifying Items.</p>
*
* @param rowIds List of SLA Qualifying Item Row IDs to update
* @param affectedCiId The affected service ID value to update
*/
public void updateAffectedCiInSlaQualifyingItems(List<Long> rowIds, Long affectedCiId)
{
logger.trace("Updating Affected Service in SLA Qualification Item Row IDs#: [{}] with Affected CI ID: {}",
rowIds.stream().map(String::valueOf).collect(Collectors.joining(",")), affectedCiId);
slaQualifyingItemsRepository.updateInBulkAffectedService(CurrentExecutionContext.getTenantIdAsInteger(), rowIds,
affectedCiId);
}
... ... ...
... ... ...
public interface SlaQualifyingItemsRepository extends JpaRepository<SlaQualifyingItemsEntity, RowId>
{
... ... ...
... ... ...
@Modifying
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=RuntimeException.class)
@Query("UPDATE SlaQualifyingItemsEntity sqie SET sqie.affectedCiId = :affectedCiId WHERE sqie.id.slice=:slice AND sqie.id.rowId IN (:rowIds)")
public void updateInBulkAffectedService(Integer slice, List<Long> rowIds, Long affectedCiId);
... ... ...
... ... ...
}