为啥我无法检索我刚刚保留的实体?

Posted

技术标签:

【中文标题】为啥我无法检索我刚刚保留的实体?【英文标题】:Why can't I retrieve the entities I've just persisted?为什么我无法检索我刚刚保留的实体? 【发布时间】:2013-06-28 19:02:28 【问题描述】:

我有这个 Web 服务,它基本上可以查询数据库并返回所有持久化的实体。出于测试目的,我创建了一个 TestDataManager,它在加载 Spring 上下文后保留 2 个示例实体(顺便说一句,我使用的是 JAX-WS、Spring、Hibernate 和 HSQLDB)。

我的 TestDataManager 如下所示:

@Component
public class TestDataManager 

@Resource
private SessionFactory sf;

@PostConstruct
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void insertTestData()
    sf.openSession();
    sf.openSession().beginTransaction();
    sf.openSession().persist(new Site("site one"));
    sf.openSession().persist(new Site("site two"));
    sf.openSession().flush();


我的 JAX-WS 端点如下所示:

@WebService
public class SmartBrickEndpoint 

@Resource
private WebServiceContext context;

public Set<Site> getSitesForUser(String user)
    return getSiteService().findByUser(new User(user));


private ISiteService getSiteService()
    ServletContext servletContext = (ServletContext) context.getMessageContext().get("javax.xml.ws.servlet.context");
    return (ISiteService) BeanRetriever.getBean(servletContext, ISiteService.class);


这是我的服务类:

@Component
@Transactional(readOnly = true)
public class SiteService implements ISiteService 

@Resource
private ISiteDao siteDao;

@Override
public Set<Site> findByUser(User user) 
    return siteDao.findByUser(user);


这是我的 DAO:

@Component
@Transactional(readOnly = true)
public class SiteDao implements ISiteDao 

@Resource
private SessionFactory sessionFactory;

@Override
public Set<Site> findByUser(User user) 
    Set<Site> sites = new LinkedHashSet<Site>(sessionFactory.getCurrentSession().createCriteria(Site.class).list());

    return sites;


这是我的 applicationContext.xml:

<context:annotation-config />
<context:component-scan base-package="br.unirio.wsimxp.dao"/>
<context:component-scan base-package="br.unirio.wsimxp.service"/>
<context:component-scan base-package="br.unirio.wsimxp.spring"/>

<bean id="applicationDS" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="jdbc:hsqldb:file:sites"/>
</bean>

<bean id="sessionFactory"
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="applicationDS" />

    <property name="configLocation">
        <value>classpath:hibernate.cfg.xml</value>
    </property>

    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.connection.release_mode">on_close</prop>
            <!--<prop key="hibernate.current_session_context_class">thread</prop>-->
            <prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
            <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
        </props>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

这就是现在的情况:

    部署应用程序时,TestDataManager#insertTestData 启动(由于@PostConstruct)并且持久化不会引发任何异常。我现在应该在数据库中有 2 个实体。 之后,我通过 SOAP 客户端调用端点,请求一直到达 DAO。 Hibernate 调用不会引发任何异常,但返回的列表是空的。

奇怪的是,在 TestDataManager 中,如果我从 sf.openSession() 切换到 sf.getCurrentSession(),我会收到一条错误消息:“No Hibernate Session bound to thread,并且配置不允许在这里创建非事务性会话” .

我在这里做错了什么?为什么查询“没有看到”持久化实体?为什么我需要在 TestDataManager 上调用 sf.openSession(),尽管它带有 @Transactional 注释?

我已经在 application.xml 中用hibernate.current_session_context_class=thread 做了一些测试,但是我只是在每个类中切换问题。我不想手动调用 sf.openSession() 并将其留给 Hibernate 来处理。

非常感谢您的帮助!

【问题讨论】:

您的问题解决了吗?您是否尝试过以下解决方案? 【参考方案1】:

我认为您需要在insertTestData 上提交事务:

@PostConstruct
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void insertTestData()
    Session session = sf.openSession();

    session.persist(new Site("site one"));
    session.persist(new Site("site two"));
    session.flush();
    session.close();

【讨论】:

我还没有尝试过,我不得不把它放在一边并用不同的方法继续前进,但奇怪的是:为什么我需要打开一个会话并开始一个 txn 如果我用@Transactional 注释了吗?这对我来说没有任何意义,毕竟 @PostConstruct 被调用了,这让我认为 spring 配置是好的,并且该类是一个“正确的”bean。这有意义吗? 不,如果您只想保留几个实体,则不需要同时开始事务并使用@Transactional。我的回答只是解决了您打开 5 个会话而不提交内部事务的事实。我已经编辑了解决方案并删除了session.beginTransaction()tx.commit()。请尝试告诉我们。【参考方案2】:

(我在jpa模式下使用hibernate)

我认为您的事务注释没有被正确拦截。您是否指定了 HibernateVendorAdapter?在 jpa+hibernate 中,没有它,集成就没有完全设置好!您很可能缺少此声明。

在您应该能够直接自动装配会话而不是工厂之后。

作为旁注。如果您在代码中使用 opensession 至少只调用一次并将会话保存在变量中。否则你总是在每次通话时打开一个新的,我相信。

【讨论】:

【参考方案3】:
@PostConstruct
public void insertTestData()
  Obejct o = new TransactionTemplate(transactionManager).execute(new TransactionCallback() 
    public Object doInTransaction(TransactionStatus status) 
      //Your code here
    
  );

来源:http://forum.springsource.org/showthread.php?58337-No-transaction-in-transactional-service-called-from-PostConstruct&p=194863#post194863

【讨论】:

我试过了,但数据只是没有显示在数据库中,是我遗漏了什么吗? gist.github.com/felipecao/5968341 是的,您缺少 session.persist。在哪里?

以上是关于为啥我无法检索我刚刚保留的实体?的主要内容,如果未能解决你的问题,请参考以下文章

核心数据添加和检索到多个表如何

为啥我无法检索响应 jQuery AJAX 请求而插入的内容的计算高度?

为啥我将主键设置为文本(字符串)时无法检索数据

无法使用 BigQuery 从 Google Datastore 检索 JSON 实体

无法使用 Nhibernate 的 Linq 检索 Group By 实体或复合键

无法从实体框架的引用表中检索数据