Kamil Pajak
Kamil Pajak

Reputation: 527

No EntityManager with actual transaction available for current thread

This error occurs at this.itemRepository.deleteAll() when sampleTest() method is run.

@ExtendWith(SpringExtension.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@SpringBootTest
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Rollback(false)
public class ScheduleTests {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleTests.class);

    @Autowired
    private ScheduleRepository scheduleRepository;

    @Autowired
    private ItemRepository itemRepository;

    @Autowired
    private ItemQualifierRepository itemQualifierRepository;

    @Autowired
    private MappingRepository mappingRepository;

    @BeforeAll
    public void setUp() {
        restoreInitialData();
        LOGGER.info("Initial data restored");
    }

    @Test
    public void sampleTest() {
        File feed = new File("target/test-classes/TestFeedOK.json");
        Feed.ingest(feed);
        LOGGER.info("Feed {} ingested", feed.getName());
    }

    private void restoreInitialData() {
        this.itemRepository.deleteAll();
        this.mappingRepository.deleteByTableName("event");
    }
}

Adding @Transactional annotation to restoreInitialData() does not solve the problem - https://stackoverflow.com/a/32552558/3536552

Do you know how to fix it?

Upvotes: 2

Views: 1738

Answers (2)

Vicky Thakor
Vicky Thakor

Reputation: 3916

By default Spring provides implementations of Interface and Class but with the test environment spring expect you to provide its implementation. So use following code to provide EntityManager implementation.

PersistenceJPAConfig.java

import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig {

    @Autowired
    private Environment environment; 

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(new String[] { "com.example.db.model" });

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(additionalProperties());
        return em;
    }

    @Bean
    public DataSource dataSource() {
        String driverClassName = environment.getProperty("spring.datasource.driverClassName");
        String url = environment.getProperty("spring.datasource.url");
        String username = environment.getProperty("spring.datasource.username");
        String password = environment.getProperty("spring.datasource.password");

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);

        return dataSource;
    }

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

        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    Properties additionalProperties() {
        Properties properties = new Properties();
        properties.setProperty("hibernate.id.new_generator_mappings","false");
        return properties;
    }
}

Upvotes: 0

pvpkiran
pvpkiran

Reputation: 27018

I think the problem is restoreInitialData is called from setup which is a @BeforeAll method. Spring hasn't stepped in at that point to have a transaction created. Just change this to @BeforeEach and check and also with @Transactional. If you want something to be executed only once. Try this approach

private static boolean setUpIsDone = false;
.....
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}

Upvotes: 1

Related Questions