草庐IT

java - 后端数据库异步更改时如何刷新JPA实体?

coder 2023-05-15 原文

我有一个 PostgreSQL 8.4 数据库,其中包含一些表和 View ,这些表和 View 本质上是对某些表的连接。我使用 NetBeans 7.2(如 here 所述)创建从这些 View 和表派生的基于 REST 的服务,并将它们部署到 Glassfish 3.1.2.2 服务器。

还有另一个进程异步更新一些用于构建 View 的表中的内容。我可以直接查询 View 和表并查看这些更改是否正确发生。但是,从基于 REST 的服务中提取时,这些值与数据库中的值不同。我假设这是因为 JPA 在 Glassfish 服务器上缓存了数据库内容的本地副本,并且 JPA 需要刷新关联的实体。

我尝试向 NetBeans 生成的 AbstractFacade 类添加几个方法:

public abstract class AbstractFacade<T> {
    private Class<T> entityClass;
    private String entityName;
    private static boolean _refresh = true;

    public static void refresh() { _refresh = true; }

    public AbstractFacade(Class<T> entityClass) {
        this.entityClass = entityClass;
        this.entityName = entityClass.getSimpleName();
    }

    private void doRefresh() {
        if (_refresh) {
            EntityManager em = getEntityManager();
            em.flush();

            for (EntityType<?> entity : em.getMetamodel().getEntities()) {
                if (entity.getName().contains(entityName)) {
                    try {
                        em.refresh(entity);
                        // log success
                    }
                    catch (IllegalArgumentException e) {
                        // log failure ... typically complains entity is not managed
                    }
                }
            }

            _refresh = false;
        }
    }

...

}

然后我调用 doRefresh()来自每个find NetBeans 生成的方法。通常发生的是 IllegalArgumentsException抛出说明类似 Can not refresh not managed object: EntityTypeImpl@28524907:MyView [ javaType: class org.my.rest.MyView descriptor: RelationalDescriptor(org.my.rest.MyView --> [DatabaseTable(my_view)]), mappings: 12]. 的东西

所以我正在寻找一些关于如何正确刷新与 View 关联的实体的建议,以便它是最新的。

更新:原来我对潜在问题的理解是不正确的。和another question I posted earlier有点关系,即 View 没有可用作唯一标识符的单个字段。 NetBeans 要求我选择一个 ID 字段,所以我只选择了应该是多部分键的一部分。这表现出具有特定 ID 字段的所有记录都相同的行为,即使数据库具有具有相同 ID 字段的记录但其余记录不同。 JPA 只是查看我告诉它的唯一标识符并简单地提取它找到的第一条记录。

我通过添加唯一标识符字段解决了这个问题(从来没有能够让多部分键正常工作)。

最佳答案

我建议添加一个 @Startup @Singleton与 PostgreSQL 数据库建立 JDBC 连接并使用 LISTEN and NOTIFY 的类处理缓存失效。

更新 :Here's another interesting approach, using pgq and a collection of workers for invalidation .

失效信号

在正在更新的表上添加一个触发器,发送 NOTIFY每当更新实体时。在 PostgreSQL 9.0 及以上版本 NOTIFY可以包含有效负载,通常是行 ID,因此您不必使整个缓存失效,只需更改已更改的实体即可。在不支持有效负载的旧版本上,您可以将无效条目添加到带时间戳的日志表中,您的助手类在获得 NOTIFY 时查询该日志表。 ,或者只是使整个缓存无效。

您现在的助手类(class) LISTEN s 上 NOTIFY触发器发送的事件。当它收到 NOTIFY事件,它可以使单个缓存条目无效(见下文),或刷新整个缓存。您可以使用 PgJDBC's listen/notify support 监听来自数据库的通知。 .您将需要解开任何管理的连接池java.sql.Connection进入底层 PostgreSQL 实现,以便您可以将其转换为 org.postgresql.PGConnection并调用 getNotifications()在上面。

替代 LISTENNOTIFY ,您可以在计时器上轮询更改日志表,并在问题表上设置触发器,将更改的行 ID 和更改时间戳附加到更改日志表。除了需要为每种 DB 类型使用不同的触发器之外,这种方法将是可移植的,但它效率低下且不及时。它将需要频繁的低效轮询,并且仍然具有监听/通知方法没有的时间延迟。在 PostgreSQL 中,您可以使用 UNLOGGED表以稍微降低这种方法的成本。

缓存级别

EclipseLink/JPA 有几个级别的缓存。

一级缓存位于 EntityManager 等级。如果实体附加到 EntityManager来自 persist(...) , merge(...) , find(...)等,然后是 EntityManager在同一 session 中再次访问该实体时,无论您的应用程序是否仍然引用它,都需要返回该实体的相同实例。如果您的数据库内容已更改,则此附加实例将不会是最新的。

二级缓存(可选)位于 EntityManagerFactory 级别,是一种更传统的缓存。不清楚您是否启用了二级缓存。检查您的 EclipseLink 日志和您的 persistence.xml .您可以使用 EntityManagerFactory.getCache() 访问二级缓存;见 Cache .

@thedayofcondor 展示了如何刷新二级缓存:

em.getEntityManagerFactory().getCache().evictAll();

但您也可以使用 evict(java.lang.Class cls, java.lang.Object primaryKey) 逐出单个对象称呼:
em.getEntityManagerFactory().getCache().evict(theClass, thePrimaryKey);

您可以从 @Startup 使用它@Singleton NOTIFY监听器仅使那些已更改的条目无效。

一级缓存不是那么容易,因为它是应用程序逻辑的一部分。您会想了解 EntityManager ,附加和分离的实体等工作。一种选择是始终对有问题的表使用分离的实体,您使用新的 EntityManager每当您获取实体时。这个问题:

Invalidating JPA EntityManager session

有一个关于处理实体管理器缓存失效的有用讨论。但是,不太可能是 EntityManager缓存是您的问题,因为 RESTful Web 服务通常使用短 EntityManager 实现 session 。如果您正在使用扩展的持久性上下文,或者您正在创建和管理自己的 EntityManager,这可能只是一个问题。 session 而不是使用容器管理的持久性。

关于java - 后端数据库异步更改时如何刷新JPA实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13258976/

有关java - 后端数据库异步更改时如何刷新JPA实体?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  10. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

随机推荐