【问题标题】:Getting old data with JPA使用 JPA 获取旧数据
【发布时间】:2011-12-17 12:44:15
【问题描述】:

我使用 JPA 获取旧数据,即使我禁用了缓存。我猜是因为资源配置为RESOURCE_LOCAL,但我不确定。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="AppPU" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>com.myentities.User</class>
        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/mydatabase"/>
            <property name="javax.persistence.jdbc.password" value="*****"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.user" value="user1"/>
            <property name="eclipselink.logging.level" value="FINEST"/>
        </properties>
    </persistence-unit>
</persistence>

我的代码正在获取有关用户的旧信息:

public List<User> findAll(App app) {       
        getEntityManager().getTransaction().begin();        
        Query q = getEntityManager().createQuery("SELECT t1 FROM User t1 WHERE t1.app.idApp=:idApp");
        q.setParameter("idApp", app.getIdApp());
        getEntityManager().flush();
        getEntityManager().getTransaction().commit();
        List resultList = q.getResultList();        
        return resultList;
    }

我的实体:

@Entity
@Table(name = "user")
@Cache (
     type=CacheType.NONE
     )
public class User implements Serializable {

// some attributtes

}

有人知道发生了什么吗?

更新 1

beginflushcommit 方法只是绝望的行为!我知道不需要。

我忘了说什么重要:我做的测试是添加一个用户记录直接在数据库控制台上然后尝试通过我的webapp查看它,这是不显示新用户。也就是我说的“老数据”,它只显示“老用户”。

我已经尝试将它放在 persistence.xml 上,但我没有看到结果有任何不同:

<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.cache.size.default" value="0"/>
<property name="eclipselink.cache.type.default" value="None"/>

还有别的东西……

【问题讨论】:

  • 1) 仅查询时不需要启动/刷新/提交任何事务。 2) 'sistema' 对象来自哪里?为什么方法中没有使用“app”参数?
  • 这就是整个方法吗?刷新和提交在那里没有任何意义。数据以什么方式陈旧?它出什么问题了?您在 EntityManager 的范围内还做了什么?打开和关闭 EM 之间的所有调用/更改是什么?有人猜测您让实体的内存状态与数据库不同步。

标签: java jpa eclipselink


【解决方案1】:

使用

<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.cache.size.default" value="0"/>
<property name="eclipselink.cache.type.default" value="None"/>

@Cache(shared=false)

【讨论】:

    【解决方案2】:

    与缓存答案(我将不得不尝试)相反,您可能会遇到引用实体未更新的情况。

    @Entity
    Class Parent
    {
      @OneToOne(Cascade.ALL)//Or only Merge, whatever you're needs
      Child child;
    }
    
    @Entity
    Class Child
    {
      Parent parent;
      ... Values
    }
    

    保存 Child 后,您需要更新对 Parent 的引用,以便内存模型(缓存)与数据库匹配。这相当令人沮丧,但我处理这个问题的方式是仅从父级级联。

    public void saveChild(Child child)
    {
      child.getParent().setChild(this); //or DTO Code, whatever
      EntityManager.merge(parent); //cascades to the child.
      //If you're manually cascading (why?) 
      //EntityManager.merge(child);
    }
    

    如果您设置它,这将级联 - 我所看到的是反向级联(子合并导致父级级联)不可靠 - 源于我缺乏该主题的知识。

    简而言之,如果您在数据层中明确处理合并,问题就会消失。我不愿意禁用缓存,因为它可能会对大型应用程序产生重大影响,因此,我选择了这条路线。祝你好运,请告诉我们你的方法。

    【讨论】:

      【解决方案3】:

      已经发布了一些建议,例如确保共享缓存已关闭,以及管理反向引用以使缓存保持一致。这些是针对可能发生的特定情况,但您没有提供足够的信息来说明实际发生的情况。

      根据您的 getEntityManager() 用法,另一个具体但似乎可能的情况是,如果您重用 EntityManager 实例而不清除它。 EntityManager 持有对所有托管实体的引用,因为 EM 需要在后续查询中返回相同的实例并查找调用以维护身份。

      如果这还没有完成,将需要清除 EntityManager 或在某些点获取一个新的以释放内存和托管实体。

      【讨论】:

      • 你是对的!问题出在我的 EntityManager 上。这与 EclipseLink Cache 或类似的东西无关。谢谢!
      • 任何人都可以请描述更多关于清除 EM 和重用它的内容。我是 JPA 的新手。我的问题是,如果我手动修改表中的两行(以测试),应用程序会获取第一个修改行的新数据,但获取第二个修改行的旧数据。我在 em.ceateNamedQuery(....) 之前做 em.clear()。可以吗?
      • @Dexter 我的建议是,如果可能的话,避免使用 DML 来执行 CUD,因为你可能会遇到Stale Data 的问题。如果需要,请在 DML 执行后致电clear()refresh(),您可以在链接中找到更多信息。
      【解决方案4】:

      首先,不要使用,

      @Cache(type=CacheType.NONE)
      or,
      <property name="eclipselink.cache.size.default" value="0"/>
      or,
      <property name="eclipselink.cache.type.default" value="None"/>
      

      刚刚设置,

      @Cache(shared=false)
      or,
      <property name="eclipselink.cache.shared.default" value="false"/>
      

      其次,您的 EntityManager 来自哪里?您是否为每个请求/事务创建一个新的?如果您不这样做,那么在 EntityManager 中读取的所有内容都将位于其 (L1) 缓存中。您需要调用 clear() 或创建一个新的。

      【讨论】:

        【解决方案5】:

        1) 优化代码

        public List<User> findAll(App app) {   
            Query q = getEntityManager().createQuery("SELECT t1 FROM User t1 WHERE    t1.app.idApp=:idApp");    q.setParameter("idApp", app.getIdApp());                 
            List resultList = q.getResultList();            
            return resultList;    
        } 
        

        2)从您的实体类中移除 @Cache (type=CacheType.NONE)

        3) 无需更改persistence.xml

        【讨论】:

          【解决方案6】:

          EntityManager 的使用是关键。几个月后我找到了完美的解决方案:

          • 对实体的所有 READS 使用通用的 DEFAULT 实体管理器。这意味着为每个实体创建一个单独的实体管理器。
          • 为每个实体的每个写入/更新/删除操作创建一个新的实体管理器。使用 begin/commit 进行该新实体的事务。操作完成后关闭实体管理器。
          • 节点点:提交后清除DEFAULT实体管理器(读取器一),关闭写入器实体管理器。这意味着只在写入后清除;不是在每次阅读之前。

          【讨论】:

            猜你喜欢
            • 2020-02-13
            • 2017-11-24
            • 2020-03-11
            • 2019-09-29
            • 1970-01-01
            • 1970-01-01
            • 2014-08-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多