May12
May12

Reputation: 2520

Why data wasn't saved when I use @Transactional annotation?

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

Answers (1)

Florian Schaetz
Florian Schaetz

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

Related Questions