Search code examples
springhibernatedaotransactional

Spring Transactional not working


I'v setup a Spring/Hibernate backend based on following example using Spring 4.1 and Hibernate 4.3.6:

http://www.sivalabs.in/2011/02/springhibernate-application-with-zero.html

I basically got everything working so far, but there is one little configuration problem I can't overcome: If I try to save an Entity I get the following StackTrace:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
at org.springframework.orm.hibernate4.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1135)
at org.springframework.orm.hibernate4.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:620)
at org.springframework.orm.hibernate4.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:617)
at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:340)
at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308)
at org.springframework.orm.hibernate4.HibernateTemplate.save(HibernateTemplate.java:617)
at com.test.database.dao.UserSecurityQuestionDAOImpl.save(UserSecurityQuestionDAOImpl.java:40)
at com.test.database.service.UserSecurityQuestionService.save(UserSecurityQuestionService.java:20)
at com.test.database.UserSecurityQuestionTest.basicCreationAndRetrievalTest(UserSecurityQuestionTest.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

My transactional directive is placed in the Service Bean and looks like this:

@Service
@Transactional
public class UserSecurityQuestionService implements UserSecurityQuestionDAO{

    @Autowired
    private UserSecurityQuestionDAO userSecurityQuerstionDAO;

    @Override
    @Transactional(readOnly=false)
    public void save(UserSecurityQuestion userSecurityQuestion) {
        userSecurityQuerstionDAO.save(userSecurityQuestion);        
    }

This is my DAO Implementation of the save Method:

@Repository
public class UserSecurityQuestionDAOImpl implements UserSecurityQuestionDAO {

    @Autowired
    private HibernateTransactionManager transactionManager; 

    public void save(UserSecurityQuestion userSecurityQuestion) {
        Session session = transactionManager.getSessionFactory().openSession();
        Transaction tx = session.beginTransaction();
        session.persist(userSecurityQuestion);
        tx.commit();
        session.close();
    }

Selecting from the Database works fine, but Inserts/Updates are refused throwing above Error Message. What am I missing?

UPDATE

UserSecurityQuestionDAOImpl:

public class UserSecurityQuestionDAOImpl implements UserSecurityQuestionDAO{

    @Autowired
    private SessionFactory sessionFactory;

    public void save(UserSecurityQuestion userSecurityQuestion) {
        this.sessionFactory.getCurrentSession().save(userSecurityQuestion);
    }

UserSecurityQuestionDAO / UserSecurityQuestionService - Interfaces:

public interface UserSecurityQuestionService {
    public void save(UserSecurityQuestion userSecurityQuestion);    
    public UserSecurityQuestion findById(Short id); 
    public List<UserSecurityQuestion> findAll();    
    public void delete(UserSecurityQuestion userSecurityQuestion);  
    public void deleteById(Short... id);    
}

Service Implementation:

@Service
public class UserSecurityQuestionServiceImpl implements UserSecurityQuestionService {

    @Autowired
    private UserSecurityQuestionDAO userSecurityQuestionDAO;    

    @Transactional
    public void save(UserSecurityQuestion userSecurityQuestion) {
        userSecurityQuestionDAO.save(userSecurityQuestion);     
    }

Unit test code Used for Testing:

public class UserSecurityQuestionTest {

    private AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();      
    private UserSecurityQuestionService usqService;

    @Before
    public void setUp(){
        //Setup App Context
        ctx.scan("com.test.database");
        ctx.refresh();

        //Initilize Service Bean
        usqService = ctx.getBean("userSecurityQuestionService",UserSecurityQuestionServiceImpl.class);
    }

New Problem: If I add @Autowired to the private UserSecurityQuestionService usqService; and omit the Setup-Part, I will get a Null pointer Exception. If I leave the code as it is, I get org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userSecurityQuestionService' is defined


Solution

  • I can see that both you service class and your dao implements same interface, and that you autowire the dao in service by its class.

    I assume your service is also autowired by its class in your controller. It would be better to have separate interfaces for service and dao to allow autowiring by type using an interface. That way, Spring will use a JDK proxy (the default) to implement the transactional proxy around your service bean.

    The alternative would be to use <tx:annotation-driven proxy-target-class="true/>", but you then need to have CGLIB in your path.