문제

I am trying to learn how to use transaction in java spring. I am a java novice so please bear with me :-) The unit test im trying to achieve below is testing: rollback if a runtime exception is thrown.

The problem im having is a java.lang.NullPointerException?

Ok here goes.. I have stripped out some code to help improve readability

TutorialTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class TutorialTest {

    @Autowired
    private Dao dao;


    @Test
    public void that_if_a_runtime_exception_is_thrown_transaction_rolledback(){

        User u = new User();
        u.setUsername("FAIL_TEST");
        u.setPhone("0161");
        u.setEmail("FAIL_TEST@gmail.com");
        //dao.addUser(u);  // -- this will insert if uncommented out so I know it works

        OuterService os  = new OuterService();
        os.addUserThrowError(u);

    }
}

OuterService.java

@ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class OuterService {

    @Autowired
    private Dao dao;

    @Transactional
    public void addUserThrowError(User user) throws RuntimeException{

        dao.addUser(user);  // gives me a java.lang.NullPointerException?

        throw new RuntimeException("This should roll back DB entry");
    }

}

Beans are declared in

AppConfig.java

@Configuration
@EnableTransactionManagement
@ComponentScan(value = {"com.training.spring.tx.tutorial.dao",
                        "com.training.spring.tx.tutorial.service"})
public class AppConfig {

    public DataSource dataSource() {

        // Create a BasicDataSource object and configure database

        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost/spring_training_tx");
        dataSource.setUsername("training");
        dataSource.setPassword("training");

        return dataSource;
    }

    @Bean
    public DataSourceTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());
        return transactionManager;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(transactionManager().getDataSource());
    }

}
도움이 되었습니까?

해결책

First of all, to quote the javadoc of @ContextConfiguration

@ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests.

Consider how you are using it on OuterService. Does it seem right? Is OuterService meant to be used to load and configure an ApplicationCOntext for integration tests? Unless I'm missing something essential, the answer is: No.

So what is OuterService? It's some kind of service. You seem to want to use it as a bean. What is a bean? A bean is an object whose lifecycle is managed by Spring. This includes instantiation of the bean class, initialization of the object, post processing, and, finally, destruction of the object.

If you created the object like so

OuterService os  = new OuterService();

then Spring is not involved. You created the object and there is no way for Spring to hook into that. You cannot therefore expect Spring to autowire its field

@Autowired
private Dao dao;

And since you haven't initialized the field, it remains null, which causes the NullPointerException.

So how do we get an OuterService bean, which is managed by Spring? You either declare a @Bean method for OuterService or you annotate OuterService with @Component or any of its specializations and component-scan the package it is in. You then inject the bean into any other bean that uses it. For example,

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class TutorialTest {

    @Autowired
    private Dao dao;

    @Autowired
    private OuterService os;

You can then use that variable directly.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top