Reputation: 2520
Colleagues, could you help me with @Transactional
annotation. My aim is to pass DB transaction management to Spring and in current case save data in DB.
I have two methods in DAO class:
@Component
public class OdValuesDAO
{
static final Logger LOG =
Logger.getLogger(OdValuesDAO.class.getName());
@Autowired
// @PersistenceContext(unitName = "PersistenceUnit")
@Qualifier("emR")
private EntityManager em;
public void addOdValue (OdValuesEntity odValuesEntity) {
LOG.info(odValuesEntity.toString());
//EntityTransaction tx = em.getTransaction(); Data will be saved DB if uncomment this code.
///tx.begin();
//LOG.info(tx.isActive());
em.persist(odValuesEntity);
///tx.commit();
}
public List<OdValuesEntity> getShare (OdValuesEntity odValuesEntity) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<OdValuesEntity> criteriaQuery = cb.createQuery(OdValuesEntity.class);
Root<OdValuesEntity> entityRoot = criteriaQuery.from(OdValuesEntity.class);
Predicate criteria = cb.conjunction();
if (odValuesEntity.getId() != null) .........
criteriaQuery.where(criteria);
List<OdValuesEntity> result = em.createQuery(criteriaQuery).getResultList();
return result;
}
and service method:
@Component
public class OdValuesService {
static final Logger LOG = Logger.getLogger(OdValuesService.class.getName());
@Autowired
private OdValuesDAO odValuesDAO;
/**
* Read data from table.
* @param odValuesEntity
* @return
*/
@Transactional (readOnly = true, value = "txtxManagerR")
public List<OdValuesEntity> getShare (OdValuesEntity odValuesEntity) {
odValuesDAO.getShare(odValuesEntity);
return odValuesDAO.getShare(odValuesEntity);
}
/** Add record to the table
*/
@Transactional (value = "txtxManagerR") /*I have two transaction managers for two different DataSources/
public void addOdValue (OdValuesEntity odValuesEntity) {
odValuesDAO.addOdValue(odValuesEntity);
}
}
And I have a test which call these two methods. I can read data from the table using getShare
method. But no data saved after calling method addOdValue
. This is my problem.
When I uncomment transaction related code in addOdValue
everything works fine. As I understood in this case Spring transaction manager doesn't work.
How to correct addOdValue
method or, may be, spring config file to save data in the database (to open and commit transaction) using Spring @Transactional annotation?
My Spring config (context) contains next beans to work with DB:
@Configuration
@EnableBatchProcessing
@EnableTransactionManagement
@ComponentScan
public class AppConfig {
@Bean
public BasicDataSource specRDataSource() {
BasicDataSource specRDataSource = new BasicDataSource();
specRDataSource.setDriverClassName("org.firebirdsql.jdbc.FBDriver");
specRDataSource.setUrl(specRDbUrl);
specRDataSource.setUsername(specRDbUser);
specRDataSource.setPassword(specRDbPassword);
specRDataSource.setMaxIdle(30);
specRDataSource.setMaxWaitMillis(10000);
specRDataSource.setValidationQuery("select 1 from rdb$database");
specRDataSource.setTestOnBorrow(false);
specRDataSource.setTestWhileIdle(true);
specRDataSource.setDefaultAutoCommit(true);
return specRDataSource;
}
@Bean
public BasicDataSource bortDataSource() {
BasicDataSource bortDataSource = new BasicDataSource();
bortDataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
bortDataSource.setUrl(bortDbUrl);
bortDataSource.setUsername(bortDbUser);
bortDataSource.setPassword(bortDbPassword);
bortDataSource.setMaxIdle(2);
bortDataSource.setMaxWaitMillis(10000);
bortDataSource.setValidationQuery("select 1");
bortDataSource.setTestOnBorrow(true);
bortDataSource.setTestWhileIdle(true);
bortDataSource.setDefaultAutoCommit(true);
return bortDataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory (BasicDataSource bortDataSource) {
LocalContainerEntityManagerFactoryBean localConnectionFactoryBean = new LocalContainerEntityManagerFactoryBean();
localConnectionFactoryBean.setPersistenceXmlLocation("classpath:META-INF/persistence.xml");
localConnectionFactoryBean.setDataSource(bortDataSource);
localConnectionFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
return localConnectionFactoryBean;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactorySpec (BasicDataSource specRDataSource) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setPersistenceXmlLocation("classpath:META-INF/persistence.xml");
emf.setDataSource(specRDataSource);
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.dialect", "org.hibernate.dialect.FirebirdDialect");
emf.setJpaPropertyMap(properties);
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setShowSql(true);
emf.setJpaVendorAdapter(hibernateJpaVendorAdapter);
return emf;
}
@Bean
public EntityManager emR (@Qualifier("entityManagerFactorySpec") EntityManagerFactory entityManagerFactorySpec) {
return entityManagerFactorySpec.createEntityManager();
}
@Bean
public EntityManager embort (@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory ) {
return entityManagerFactory.createEntityManager();
}
@Bean
public JpaTransactionManager txtxManagerR (@Qualifier("entityManagerFactorySpec") EntityManagerFactory entityManagerFactorySpec) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactorySpec);
return txManager;
}
@Bean
public JpaTransactionManager txManagerbort (@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
My persistence.xml looks like (if need):
<persistence-unit name="PersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.acap.app.JpaEntities.OdDocsEntity</class>
<class>com.acap.app.JpaEntities.OdValuesEntity</class>
.......
<class>com.acap.app.JpaEntities.OdOSharesEntity</class>
<properties>
<property name="hibernate.connection.url"
value="connection string"/>
<property name="hibernate.connection.driver_class" value="org.firebirdsql.jdbc.FBDriver"/>
<property name="hibernate.connection.username"/>
<property name="hibernate.connection.password"/>
<property name = "hibernate.show_sql" value = "false" />
<property name = "hibernate.format_sql" value = "false" />
</properties>
</persistence-unit>
Stack trace doesn't show any error. Thank you for helping.
UPDATE Test running:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class, loader = AnnotationConfigContextLoader.class)
public class OdValuesServiceTest {
static final Logger LOG = Logger.getLogger(OdValuesServiceTest.class.getName());
@Autowired
OdValuesService odValuesService;
@Autowired
DBCommonsService dbCommonsService;
@Commit
@Test
public void addOdValue() throws Exception {
OdValuesEntity odValuesEntity = new OdValuesEntity();
odValuesEntity.setId(dbCommonsService.getNextDocOD("OD_VALUES_ID_GEN"));
odValuesEntity.setSysname("Name" + DataGenerator.getRandomISIN());
odValuesEntity.setName("Name");
odValuesEntity.setIsIn((short) 1);
odValuesEntity.setvType(2);
odValuesEntity.setMfu((short) 0);
odValuesEntity.setIsin("AU000A0JP922");
odValuesEntity.setCfi("");
odValuesService.addOdValue(odValuesEntity);
}
}
Upvotes: 2
Views: 2477
Reputation: 10662
From the spring documentation:
15.2.3 Transaction management
[..]By default, the framework will create and roll back a transaction for each test.[..]
So, if your "problem" is, that no data is written to the database in your tests when using @Transactional
, then that's no problem at all, it's how the spring default works. If that's not what you want...
If you want a transaction to commit — unusual, but occasionally useful when you want a particular test to populate or modify the database — the TestContext framework can be instructed to cause the transaction to commit instead of roll back via the @Commit annotation.
Upvotes: 3