【问题标题】:Spring Transactional not workingSpring事务不工作
【发布时间】:2014-11-25 02:14:41
【问题描述】:

我使用 Spring 4.1 和 Hibernate 4.3.6 根据以下示例设置了 Spring/Hibernate 后端:

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

到目前为止,我基本上一切正常,但有一个小配置问题我无法克服:如果我尝试保存一个实体,我会得到以下 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)

我的事务指令放置在服务 Bean 中,如下所示:

@Service
@Transactional
public class UserSecurityQuestionService implements UserSecurityQuestionDAO{

    @Autowired
    private UserSecurityQuestionDAO userSecurityQuerstionDAO;

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

这是我的保存方法的DAO实现:

@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();
    }

从数据库中选择工作正常,但插入/更新被拒绝抛出错误消息。我错过了什么?

更新

UserSecurityQuestionDAOImpl:

public class UserSecurityQuestionDAOImpl implements UserSecurityQuestionDAO{

    @Autowired
    private SessionFactory sessionFactory;

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

UserSecurityQuestionDAO / UserSecurityQuestionService - 接口:

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
public class UserSecurityQuestionServiceImpl implements UserSecurityQuestionService {

    @Autowired
    private UserSecurityQuestionDAO userSecurityQuestionDAO;    

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

用于测试的单元测试代码:

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);
    }

新问题:如果我将@Autowired 添加到private UserSecurityQuestionService usqService; 并省略设置部分,我将得到一个空指针异常。如果我保持原样,我会得到org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userSecurityQuestionService' is defined

【问题讨论】:

  • 您没有交易。在您的配置中添加&lt;tx:annotation-driven /&gt;@EnableTransactionManagement。此外,您不应该使用HibernaetTemplate,而应该使用SessionFactory.getCurrentSession(实际上这是自Hibernate 3.0.1 以来的建议!)。
  • 好的,我刚刚注意到我用作模板的示例并不是那么新。检查我更新的帖子,现在配置正确了吗?
  • 否,因为您仍在使用手动 tx 管理,实际上通过注入事务管理器使情况变得更糟。注入SessionFactory 而不是openSession 使用getCurrentSession 并删除所有事务性内容和会话关闭。你的方法应该是 2 行。
  • 好吧,我想我现在差不多了(见更新)。只是不知何故,自动布线似乎在单元测试中正常工作。
  • 不要自己动手,而是使用弹簧测试支持。此外,bean 将以小写首字母的类名命名,因此 UserSecurityQuestionServiceImpl 变为 userSecurityQuestionServiceImpl 并且您应该使用接口而不是具体类(由于创建了动态代理来应用事务,后者将不起作用)。

标签: spring hibernate dao transactional


【解决方案1】:

我可以看到你的服务类和你的 dao 都实现了相同的接口,并且你按照它的类在服务中自动装配 dao。

我假设您的服务也由您的控制器中的类自动装配。最好为 service 和 dao 提供单独的接口,以允许使用接口按类型自动装配。这样,Spring 将使用 JDK 代理(默认)围绕您的服务 bean 实现事务代理。

替代方法是使用&lt;tx:annotation-driven proxy-target-class="true/&gt;",但您需要在路径中添加CGLIB。

【讨论】:

  • 因此,如果我的建议正确,我应该有一个名为“UserSecurityQuestionService”的接口,该接口在我的“UserSecurityQuestionServiceImpl”类中实现,另一个名为“UserSecurityQuestionDAO”的接口由“UserSecurityQuestionDAOImpl”实现。我现在应该在哪里放置哪些注释?
  • 可以将注解留在实现类中。来自 Spring 框架参考手册(事务章节):Spring 建议您仅使用 @Transactional 注释来注释具体类(和具体类的方法),而不是注释接口。
  • 好的,现在我收到以下错误:org.springframework.beans.factory.BeanNotOfRequiredTypeException: 名为 'userSecurityQuestionService' 的 Bean 必须是 [com.test.database.service.UserSecurityQuestionService] 类型,但是是实际上是 [com.sun.proxy.$Proxy30] 类型,这一定是你之前一直在谈论的 Proxy-Stuff。我更新了我上面的帖子,所以你可以看到当前的设置。现在使用 UserSecurityQuestionDAO 而不是 UserSecurityQuestionDAOImpl。我这样做对吗?
猜你喜欢
  • 2017-09-21
  • 2019-01-10
  • 2013-03-08
  • 2016-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-20
  • 2011-05-16
相关资源
最近更新 更多