My service method is annotated with Transnational (org.springframework.transaction.annotation)
But still a "TransactionRequiredException" occurs when making an update on table Persons.
A select on the same table works fine.
#Transactional
public String myMethod(String contractNo){
myRepository.resetValues(contractNo);
}
#Repository
public interface MyRepository extends JpaRepository<Persons, Long> {
#Modifying
#Query(value = "UPDATE PERSONS SET status = 0 WHERE LOGIN_NAME like :contractNo", nativeQuery = true)
void resetValues(#Param("contractNo") String contractNo);
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
basePackages = { "com.mypackage.repositories" }
)
public class EBankingDBConfig {
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.eba-datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSource") DataSource dataSource) {
return builder.dataSource(dataSource)
.packages("com.mypackage.model")
.persistenceUnit("myPersistenceUnit")
.build();
}
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Because there are multiple TransactionManagers beans defined in the project, you have to annotate transactions of non-primary TransactionaManagers with their names. For example, in my config above, the TransactionManager name is defined as "transactionManager".
So, the Transactional annotation on methods should look line this : #Transactional("transactionManager")
Related
I have added an additional datasource in Spring batch project.
When I am trying to run the job locally its running successfully I am having issue on running Integration testcases.
I have data.sql script which inserting data into table. It does not have any sql script to create table as I have added in test properties "spring.jpa.hibernate.ddl-auto=create-drop"
test/resources/application.properties
first.datasource.jdbc-url=jdbc:h2:mem:testdb
first.datasource.driverClassName=org.h2.Driver
first.datasource.username=username
first.datasource.password=password
first.datasource.hikari.connectionTimeout=400000
first.datasource.hikari.maximum-pool-size=600000
first.datasource.hikari.minimum-idle=5
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.jdbc.batch_size=10
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.batch_versioned_data=true
spring.batch.initialize-schema=always
spring.batch.job.enabled=false
second.datasource.jdbc-url=jdbc:h2:mem:testdb
second.datasource.driverClassName=org.h2.Driver
second.datasource.username=username
second.datasource.password=password
second.datasource.hikari.connectionTimeout=400000
second.datasource.hikari.maximum-pool-size=600000
second.datasource.hikari.minimum-idle=5
Configuration file for first Datasource is
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "firstappEntityManagerFactory",
transactionManagerRef = "firstappTransactionManager",
basePackages = "com.app.repository"
)
#EnableTransactionManagement
public class firsDataSourceDBConfig {
#Bean(name="firstappDataSource")
#Primary
#ConfigurationProperties(prefix = "first.datasource")
public DataSource appDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "firstappEntityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean appEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("firstappDataSource") DataSource appDataSource){
return builder
.dataSource(appDataSource)
.packages("com.app.domain")
.persistenceUnit("firstUnit")
.build();
}
#Bean(name = "firstappTransactionManager")
#Primary
public PlatformTransactionManager appTransactionManager(#Qualifier("firstappEntityManagerFactory") EntityManagerFactory
appEntityManagerFactory) {
return new JpaTransactionManager(appEntityManagerFactory);
}
#Bean
public BatchConfigurer configurer(#Qualifier("firstappEntityManagerFactory") EntityManagerFactory appEntityManagerFactory) {
return new JpaTransactionManager(appEntityManagerFactory);
}
}
Second one is without BatchConfigurer beans
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "secondappEntityManagerFactory",
transactionManagerRef = "secondappTransactionManager",
basePackages = "com.abc.repository"
)
#EnableTransactionManagement
public class firsDataSourceDBConfig {
#Bean(name="secondappDataSource")
#Primary
#ConfigurationProperties(prefix = "second.datasource")
public DataSource appDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "secondappEntityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean appEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("secondappDataSource") DataSource appDataSource){
return builder
.dataSource(appDataSource)
.packages("com.app.domain")
.persistenceUnit("secondUnit")
.build();
}
#Bean(name = "secondappTransactionManager")
#Primary
public PlatformTransactionManager appTransactionManager(#Qualifier("secondappEntityManagerFactory") EntityManagerFactory
appEntityManagerFactory) {
return new JpaTransactionManager(appEntityManagerFactory);
}
}
}
Files with annotation #DataJpaTest are running successfully
#RunWith(SpringRunner.class)
#DataJpaTest
public class RepositoryTest {
#Autowired
private FirstDataSourceRepository repo;
..........
}
Test cases which are annotated with #SpringBootTest are failing
#RunWith(SpringRunner.class)
#SpringBootTest
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class SpringBatchIntegrationTest {
#Autowired
private FirstDataSourceRepository repo;
..........
}
Error stating BeanCreation Exception
firstappEntityManagerFactory.....org.h2.jdbc.JdbcSQLException: Table "TESTING" not found
I use Springboot JPA to create multiple configuration with multiple dataSource. I created one configuration for Oracle database like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "mypackages.models.another",
entityManagerFactoryRef = "anotherEntityManagerFactory",
transactionManagerRef = "anotherTransactionManager"
)
public class AnotherJpaConfiguration {
#Qualifier("anotherDataSource")
#Autowired
DataSource dataSource;
#Bean(name = "anotherEntityManager")
public EntityManager entityManager() {
return anotherEntityManagerFactory().createEntityManager();
}
#Bean(name = "anotherEntityManagerFactory")
public EntityManagerFactory anotherEntityManagerFactory() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle12cDialect");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setJpaVendorAdapter(vendorAdapter);
emf.setPackagesToScan(new String[] { "mypackages.models.destination" }); // <- package for entities
emf.setPersistenceUnitName("anotherPersistenceUnit");
emf.setJpaProperties(properties);
emf.afterPropertiesSet();
return emf.getObject();
}
#Bean(name = "anotherTransactionManager")
public PlatformTransactionManager anotherTransactionManager() {
return new JpaTransactionManager(anotherEntityManagerFactory());
}
}
I used the entityManager to execute a native query:
#Service
#Slf4j
public class DestinationServiceImpl extends DestinationService{
#Qualifier("anotherEntityManager")
#Autowired
public EntityManager em2;
#Transactional("anotherTransactionManager")
#Override
void saveMtdoDictRefrQuery() {
joinTransaction();
String query = "MY INSERT QUERY)";
List<Object> result2 = this.em2.createNativeQuery("MY SELECT QUERY").getResultList();
log.debug(String.valueOf(result2.size())); // it's work
this.em2.createNativeQuery(query).executeUpdate(); // doesn't work
}
}
But I have this error:
javax.persistence.TransactionRequiredException: Executing an update/delete query
Any idea?
Thank you
I have an application in Spring Boot 1.4 which I'm trying to add additional datasources to.
First I setup a primary datasource and ran the application to check it still worked, and it did. Then I went ahead and added a second datasource, but when I did that I got the following error;
Description:
Field userRepo in com.nationallocums.config.CustomUserDetailsService required a single bean, but 2 were found:
- nlDoctorsEntityManager: defined by method 'nlDoctorsEntityManager' in class path resource [com/nationallocums/config/NLDoctorsDataSourceConfiguration.class]
- primaryEntityManager: defined by method 'primaryEntityManager' in class path resource [com/nationallocums/config/PrimaryDataSourceConfiguration.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
I don't understand why I'm seeing this error, as I've clearly marked one of the datasources with #Primary, but it seems Spring Boot isn't picking that up.
Here's my two datasource configurations;
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryTransactionManager",
basePackages = { "com.nationallocums.repository" })
#EnableTransactionManagement
public class PrimaryDataSourceConfiguration {
#Bean(name = "primaryDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
#Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "primaryEntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManager(final EntityManagerFactoryBuilder builder, #Qualifier("primaryDataSource") final DataSource dataSource) {
final Map<String, String> properties = new HashMap<>();
return builder
.dataSource(dataSource)
.properties(properties)
.packages("com.nationallocums.model")
.persistenceUnit("primary")
.build();
}
#Bean(name = "primaryTransactionManager")
#Primary
public PlatformTransactionManager nlDoctorsTransactionManager(#Qualifier("primaryEntityManager") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
and...
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "nlDoctorsEntityManager",
transactionManagerRef = "nlDoctorsTransactionManager",
basePackages = { "com.nationallocums.eclipse.nldoctorsrepository" })
#EnableTransactionManagement
public class NLDoctorsDataSourceConfiguration {
#Bean(name = "nlDoctorsDataSource")
#ConfigurationProperties(prefix = "spring.nldoctors-datasource")
public DataSource nlDoctorsDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "nlDoctorsEntityManager")
public LocalContainerEntityManagerFactoryBean nlDoctorsEntityManager(final EntityManagerFactoryBuilder builder, #Qualifier("nlDoctorsDataSource") final DataSource dataSource) {
final Map<String, String> properties = new HashMap<>();
return builder
.dataSource(dataSource)
.properties(properties)
.packages("com.nationallocums.eclipse.model")
.persistenceUnit("nlDoctors")
.build();
}
#Bean(name = "nlDoctorsTransactionManager")
public PlatformTransactionManager nlDoctorsTransactionManager(#Qualifier("nlDoctorsEntityManager") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Can anyone spot what I've done wrong?
I managed to fix this by changing my repository from...
#PersistenceContext
private EntityManager entityManager;
to...
#Autowired
#Qualifier("primaryDataSource")
private EntityManager entityManager;
I'm writing a SpringBoot REST server. I have some problems with the data access layer. In my saveReport() service method, if I call save() method of JPA repository, I can't see any records in the db. If I use saveAndFlush() method of JPA repository, I get the following exception:
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
Db config class:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "qaEntityManagerFactory",
basePackages = { "qa.repository" }
)
public class QADbConfig {
#Bean(name = "qaDataSource")
#ConfigurationProperties(prefix = "spring.qa-datasource")
public HikariDataSource dataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
#Bean(name = "qaEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean
entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("qaDataSource") DataSource dataSource
) {
return builder
.dataSource(dataSource)
.packages("qa.model")
.persistenceUnit("qa")
.build();
}
#Bean(name = "qaTransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("qaEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
In my service layer, I added #Transactional
#Service
public class ReportService implements IReportService {
static final ObjectMapper objectMapper = new ObjectMapper();
#Autowired
ReportRepository reportJpaRepository;
#Transactional(propagation=Propagation.REQUIRED)
#Override
public Report saveReport(Report report) {
reportJpaRepository.save(report); // I get no transaction exception if I use saveAndFlush method here
return report;
}
Also in the repository class, I added #Transactional
#Transactional
#Repository
public interface ReportJpaRepository extends JpaRepository<Report, Integer> {
List<Report> findByPrivateReport(Boolean privateReport);
}
Do you see what is wrong with my code?
Thanks
I am using Spring JPA Repository to connect to Oracle.
My Repository package is com.demo.infrastructure.repository;
Repository class is StoreRepo.java
#Repository
public interface StoreRepo extends JpaRepository<StoreAttribute, String> {
#Query("select storeAttributeName from StoreAttribute order by storeAttributeName asc")
List<String> fetchAllStoreAttributeNames();
List<StoreAttribute> findAllByOrderByStoreAttributeNameAsc();
}
Problem:
I am using JNDI config to configure data source. Currently it has only one JNDI entry. Now I want to use two user names for the same database, one with admin(read-write) access and the other with user(read-only) access. Both these users will access the same Repository and same entity.
I tried the solutions already available which uses two different repository packages for each data source. But I want the Repository "StoreRepo" to be the same.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryAdmin",
basePackages = { "com.demo.infrastructure.repository" }
)
public class DataSourceAdminConfig {
#Primary
#Bean(name = "dataSourceAdmin")
public DataSource dataSource() {
return new JndiDataSourceLookup().getDataSource("jdbc/myds_admin");
}
#Primary
#Bean(name = "entityManagerFactoryAdmin")
public LocalContainerEntityManagerFactoryBean
entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSourceAdmin") DataSource dataSource
) {
return builder.dataSource(dataSource).
packages("com.demo.domain.model.entities").
persistenceUnit("read-write").
build();
}
#Primary
#Bean(name = "transactionManagerAdmin")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactoryAdmin") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
I should have two classes like this with different package (Refer basePackages). But I dont want this solution instead want to use single repository package and the same repository class.
Solution that worked for me.
1) Created separate config classes one for admin user and app user each
2) Created seprate entity manager references one for admin user and app user each
3) Instantiated the same Repositoy class (without using #Repository annotation) through java code and using respective entity manager
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryAdmin"
)
public class AdminUserConfig {
#Primary
#Bean(name = "dataSourceAdmin")
public DataSource dataSourceAdmin(#Value("${spring.datasource.admin-user.jndi-name}") String key) {
return new JndiDataSourceLookup().getDataSource(key);
}
#Primary
#Bean(name = "entityManagerFactoryAdmin")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryAdmin(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSourceAdmin") DataSource dataSource
) {
return builder.dataSource(dataSource).
packages("com.demo.domain.model.entities").
persistenceUnit("read-write").
build();
}
#Bean(name = "entityManagerAdmin")
public EntityManager entityManagerAdmin(#Qualifier("entityManagerFactoryAdmin") EntityManagerFactory
entityManagerFactory) {
return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
}
#Bean(name = "adminRepository")
public StoreRepo readWriteDimStoreRepository(#Qualifier("jpaRepositoryFactoryAdmin")
JpaRepositoryFactory repositoryFactory) {
return repositoryFactory.getRepository(StoreRepo.class);
}
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryApp"
)
public class AppUserConfig {
#Primary
#Bean(name = "dataSourceApp")
public DataSource dataSourceApp(#Value("${spring.datasource.App-user.jndi-name}") String key) {
return new JndiDataSourceLookup().getDataSource(key);
}
#Primary
#Bean(name = "entityManagerFactoryApp")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryApp(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSourceApp") DataSource dataSource
) {
return builder.dataSource(dataSource).
packages("com.demo.domain.model.entities").
persistenceUnit("read-only").
build();
}
#Bean(name = "entityManagerAdmin")
public EntityManager entityManagerApp(#Qualifier("entityManagerFactoryApp") EntityManagerFactory
entityManagerFactory) {
return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
}
#Bean(name = "AppRepository")
public StoreRepo readOnlyStoreRepository(#Qualifier("jpaRepositoryFactoryApp")
JpaRepositoryFactory repositoryFactory) {
return repositoryFactory.getRepository(StoreRepo.class);
}
}
//#Repository
public interface StoreRepo extends JpaRepository<StoreAttribute, String> {
#Query("select storeAttributeName from StoreAttribute order by
storeAttributeName asc")
List<String> fetchAllStoreAttributeNames();
List<StoreAttribute> findAllByOrderByStoreAttributeNameAsc();
}