gpt4 book ai didi

jdo - 避免 DataNucleus 连接?

转载 作者:行者123 更新时间:2023-12-04 06:50:16 25 4
gpt4 key购买 nike

我正在尝试将 JDBC webapp 移动到 JDO DataNucleus 2.1.1。

假设我有一些看起来像这样的类:

公共(public)类职位{
私有(private)整数 id;
私有(private)字符串标题;
}

公共(public)类员工{
私有(private)整数 id;
私有(private)字符串名称;
私有(private)职位职位;
}

Position SQL 表的内容确实不会经常更改。使用 JDBC,我将整个表读入内存(能够定期或随意刷新)。然后,当我将 Employee 读入内存时,我只需从 Employee 表中检索职位 ID 并使用它来获取内存中的 Position 实例。

但是,使用 DataNucleus,如果我遍历所有位置:

Extent<Position> extent =pm.getExtent(Position.class, true);
Iterator<Position> iter =extent.iterator();
while(iter.hasNext()) {
Position position =iterPosition.next();
System.out.println(position.toString());
}

然后稍后,使用不同的 PersistenceManager 遍历所有员工,获得他们的职位:
Extent<Employee> extent =pm.getExtent(Employee.class, true);
Iterator<Employee> iter =extent.iterator();
while(iter.hasNext()) {
Employee employee =iter.next();
System.out.println(employee.getPosition());
}

然后,当我获得 Employee's Position 时,DataNucleus 似乎会生成连接两个表的 SQL:

选择 A0.POSITION_ID,B0.ID,B0.TITLE 从 MYSCHEMA.EMPLOYEE A0 LEFT OUTER JOIN MYSCHEMA."POSITION"B0 ON A0.POSITION_ID = B0.ID WHERE A0.ID = <1>

我的理解是,DataNucleus 将在可用时使用缓存的 Position 实例。 (对吗?)但是,我担心连接会降低性能。我还远远不够运行基准测试。我的恐惧是不是错位了?我应该继续并进行基准测试吗?有没有办法让 DataNucleus 避免加入?
<jdo>
<package name="com.example.staff">
<class name="Position" identity-type="application" schema="MYSCHEMA" table="Position">
<inheritance strategy="new-table"/>
<field name="id" primary-key="true">
<column name="ID" jdbc-type="integer"/>
</field>
<field name="title">
<column name="TITLE" jdbc-type="varchar"/>
</field>
</class>
</package>
</jdo>

<jdo>
<package name="com.example.staff">
<class name="Employee" identity-type="application" schema="MYSCHEMA" table="EMPLOYEE">
<inheritance strategy="new-table"/>
<field name="id" primary-key="true">
<column name="ID" jdbc-type="integer"/>
</field>
<field name="name">
<column name="NAME" jdbc-type="varchar"/>
</field>
<field name="position" table="Position">
<column name="POSITION_ID" jdbc-type="int" />
<join column="ID" />
</field>
</class>
</package>
</jdo>

我想我希望能够做的是告诉 DataNucleus 继续读取 POSITION_ID int 作为默认提取组的一部分,并查看相应的 Position 是否已被缓存。如果是这样,则设置该字段。如果没有,则稍后再加入,如果需要的话。更好的是,继续将该 int ID 存储在某处,并在稍后调用 getPosition() 时使用它。这将避免在所有情况下加入。

我认为知道类和主键值就足以避免幼稚的情况,但我对 DataNucleus 的了解还不够。

有了收到的有用反馈,我的 .jdo 现已清理完毕。但是,在将 POSITION_ID 字段添加到默认提取组后,我仍然得到一个加入。
SELECT 'com.example.staff.Employee' AS NUCLEUS_TYPE,A0.ID,A0."NAME",A0.POSITION_ID,B0.ID,B0.TITLE FROM MYSCHEMA.EMPLOYEE A0 LEFT OUTER JOIN MYSCHEMA."POSITION" B0 ON A0.POSITION_ID = B0.ID

我理解它为什么这样做,天真的方法总是有效的。我只是希望它能够做得更多。尽管 DataNucleus 可能不会从结果集中读取所有列,而是返回缓存的位置,但它仍然调用数据存储来访问第二个表,包括可能的磁盘查找和读取。它会抛弃这项工作的事实并没有什么安慰。

我希望做的是告诉 DataNucleus 所有位置都将被缓存,相信我。如果由于某种原因你找到了一个不是,请怪我缓存未命中。我了解您必须(透明地)在职位表上执行单独的选择。 (更好的是,固定由于缓存未命中而必须获取的任何位置。这样就不会再次在对象上发生缓存未命中。)

这就是我现在通过 DAO 使用 JDBC 所做的事情。研究持久层的原因之一是放弃这些 DAO。很难想象迁移到无法超越原始获取的持久层会导致昂贵的连接。

只要 Employee 不仅有一个职位,而且有一个部门和其他字段,一个 Employee 提取会导致访问六个表,即使所有这些对象都已经固定在缓存中,并且可以根据它们的类和首要的关键。事实上,我可以自己实现这一点,将 Employee.position 更改为 Integer,创建 IntIdentity,并将其传递给 PersistenceManager.getObjectByID()。

我想我听到的是 DataNucleus 无法进行这种优化。是对的吗?很好,只是不是我所期望的。

最佳答案

默认情况下,从数据存储中获取 Employee 实体时不会进行连接,只有在实际读取 Employee.position 时才会进行连接(这称为延迟加载)。

此外,使用 level 2 cache 可以避免第二次提取。 .首先检查二级缓存是否实际启用(在 DataNucleus 1.1 中默认禁用,在 2.0 中默认启用)。然后,您可能应该“固定”该类,以便将其位置实体无限期地缓存:

但是,如果其他应用程序使用相同的数据库,二级缓存可能会导致问题,因此我建议仅对很少更改的类(例如位置)启用它。对于其他类,将“cacheable”属性设置为 false(默认为 true)。

编辑添加:

元数据中的 标记不适合这种情况。事实上,您根本不需要明确指定关系,DataNucleus 会从类型中找出它。但是,当您说需要在默认提取组中读取 POSITION_ID 时,您是对的。这一切都可以通过对元数据进行以下更改来实现:

<field name="position" default-fetch-group="true">
<column name="POSITION_ID" jdbc-type="int" />
</field>

编辑添加:

澄清一下,在进行上述元数据更改后,我运行了您提供的测试代码(由 MySQL 数据库支持),我只看到了这两个查询:
SELECT 'com.example.staff.Position' AS NUCLEUS_TYPE,`THIS`.`ID`,`THIS`.`TITLE` FROM `POSITION` `THIS` FOR UPDATE
SELECT 'com.example.staff.Employee' AS NUCLEUS_TYPE,`THIS`.`ID`,`THIS`.`NAME`,`THIS`.`POSITION_ID` FROM `EMPLOYEE` `THIS` FOR UPDATE

如果我只运行代码的第二部分(Employee 范围),那么我只会看到第二个查询,根本无法访问 POSITION 表。为什么?因为 DataNucleus 最初提供“空心” Position 对象,而从 Object 继承的 Position.toString() 的默认实现不访问任何内部字段。如果我重写 toString() 方法以返回职位的标题,然后运行示例代码的第二部分,那么对数据库的调用是:
SELECT 'com.example.staff.Employee' AS NUCLEUS_TYPE,`THIS`.`ID`,`THIS`.`NAME`,`THIS`.`POSITION_ID` FROM `EMPLOYEE` `THIS` FOR UPDATE
SELECT `A0`.`TITLE` FROM `POSITION` `A0` WHERE `A0`.`ID` = <2> FOR UPDATE
SELECT `A0`.`TITLE` FROM `POSITION` `A0` WHERE `A0`.`ID` = <1> FOR UPDATE

(依此类推,每个位置实体一次获取)。如您所见,没有执行任何连接,因此听到您的体验不同,我感到很惊讶。

关于您希望缓存应该如何工作的描述,这就是当一个类被固定时二级缓存应该如何工作。事实上,我什至不会在应用程序启动时尝试将 Position 对象预加载到缓存中。让 DN 累积缓存它们。

确实,如果您采用 JDO,您可能不得不接受一些妥协……您将不得不放弃使用手动滚动的基于 JDBC 的 DAO 获得的绝对控制权。但在这种情况下,至少你应该能够实现你想要的。它确实是 2 级缓存的典型用例之一。

关于jdo - 避免 DataNucleus 连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3224584/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com