我正在尝试将JDBC webapp移动到JDO DataNucleus 2.1.1。
假设我有一些看起来像这样的类:
公共课位置{ private Integer id; 私有字符串标题; }
公共类员工{ private Integer id; 私有字符串名称; 私人职位; }
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,遍历所有Employees,获得他们的位置:
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());
}
然后,当我获得员工的职位时,DataNucleus似乎会产生连接两个表的SQL:
SELECT A0.POSITION_ID,B0.ID,B0.TITLE from MYSCHEMA.EMPLOYEE A0 LEFT OUTER JOIN MYSCHEMA。“POSITION”B0 ON A0.POSITION_ID = B0.ID WHERE A0.ID =&lt; 1&gt;
我的理解是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所有职位将被缓存,相信我。如果由于某种原因你发现一个不是,请责怪我的缓存未命中。我知道你必须(透明地)在Position表上执行单独的选择。 (更好的是,将任何位置固定,因为缓存未命中而必须进行获取。这样就不会再次出现对象的缓存错误。)
这就是我现在正在使用JDBC,通过DAO。调查持久层的原因之一是抛弃这些DAO。很难想象移动到一个持久层,它不能超越天真的提取,导致昂贵的连接。
只要Employee不仅有一个Position,一个Department和其他字段,Employee fetch就会导致访问六个表,即使所有这些对象已经固定在缓存中,并且可以寻址他们的班级和主键。实际上,我可以自己实现,将Employee.position更改为Integer,创建IntIdentity,并将其传递给PersistenceManager.getObjectByID()。
我认为我听到的是DataNucleus无法进行此优化。是对的吗?这很好,只是不是我的预期。
答案 0 :(得分:2)
答案 1 :(得分:1)
再加上托德的回复,澄清一些事情。
A&lt; join&gt; 1-1关系上的标记意味着什么。好吧,它可以被解释为“创建一个连接表来存储这种关系”,但是然后DataNucleus不支持这样的概念,因为最佳做法是在所有者或相关表中使用FK。因此,请删除&lt; join&gt;
1-1关系中的“表”表示它存储在辅助表中,但您也不想要它,所以将其删除。
您检索位置对象,因此会发出类似
SELECT 'org.datanucleus.test.Position' AS NUCLEUS_TYPE,A0.ID,A0.TITLE FROM "POSITION" A0
SELECT 'org.datanucleus.test.Employee' AS NUCLEUS_TYPE,A0.ID,A0."NAME" FROM EMPLOYEE A0
请注意,它不会检索此处位置的FK,因为该字段不在默认提取组中(延迟加载)
SELECT A0.POSITION_ID,B0.ID,B0.TITLE FROM EMPLOYEE A0 LEFT OUTER JOIN "POSITION" B0 ON A0.POSITION_ID = B0.ID WHERE A0.ID = ?
此时它不需要检索Position对象,因为它已经存在(在缓存中),因此返回该对象。
所有这些都是预期的行为恕我直言。您可以将Employee的“position”字段放入其默认的提取组中,并在步骤4中检索FK,从而删除一个SQL调用。