我正在尝试将JDBC迁移到JDO DataNucleus 2.1.1。
假设我有一些类如下所示:
公共类位置{私有整数id;私有字符串标题;}
公共类雇员{私有整数id;私有字符串名称;私有职位位置;}
位置SQL表的内容实际上并不经常更改。使用JDBC,我将整个表读入内存(具有定期或随意刷新的能力)。然后,当我将雇员读入内存时,我只需从Employee表中检索位置ID,并使用该ID获取内存中的位置实例。
但是,使用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());
}然后,当我获得员工的职位时,DataNucleus似乎会生成将这两个表连接起来的SQL:
选择A0.POSITION_ID,B0.ID,B0.TITLE从MYSCHEMA.EMPLOYEE A0左转加入MYSCHEMA。在A0.POSITION_ID = B0.ID中“B0”位置,其中A0.ID = <1>
我的理解是,如果可用,DataNucleus将使用缓存的位置实例。(对吗?)但是,我担心联接会降低性能。我还没有足够的时间来运行基准测试。我的恐惧是不是错了?我应该继续,并基准吗?有办法让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作为默认fetch组的一部分,并查看相应的位置是否已经缓存。如果是,那么设置该字段。如果没有,则在调用时稍后进行连接。更好的是,继续将int藏在某个地方,并在稍后调用getPosition()时使用它。这样在任何情况下都可以避免加入。
我认为了解类和主键值就足以避免这种天真的情况,但我对DataNucleus还不太了解。
有了我收到的有用的反馈,我的.jdo现在被清理干净了。但是,在将POSITION_ID字段添加到默认的fetch组之后,我仍然会得到一个join。
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不仅有一个职位,而且有一个Department和其他字段,Employee fetch就会导致访问六个表,即使所有这些对象都已经被固定在缓存中,并且根据它们的类和主键可以寻址。实际上,我可以自己实现这一点,将Employee.position更改为Integer,创建IntIdentity,并将其传递给PersistenceManager.getObjectByID()。
我所听到的是,DataNucleus无法进行这种优化。是那么回事吗?没关系,只是不像我想的那样。
发布于 2010-07-12 06:51:03
默认情况下,当从数据存储中获取Employee实体时,不会进行连接,只有在实际读取Employee.position时才会这样做(这称为延迟加载)。
此外,可以使用二级缓存避免第二次获取。首先检查是否实际启用了2级缓存(在DataNucleus 1.1中,默认情况下禁用它,在2.0中默认启用它)。然后,您可能应该“锁定”类,以便无限期地缓存它的位置实体:
但是,如果其他应用程序使用相同的数据库,则级别2缓存可能会导致问题,因此,我建议只对很少更改的类(如位置)启用该缓存。对于其他类,将"cacheable“属性设置为false (默认值为true)。
编辑以添加:
元数据中的标记不适合这种情况。实际上,您根本不需要显式地指定关系,DataNucleus将从类型中找出它。但是,当您说需要在默认的fetch组中读取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如果我只运行代码的第二部分(雇员范围),那么我只会看到第二个查询,根本无法访问POSITION表。为什么?因为DataNucleus最初提供“空心”位置对象,并且继承自对象的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(以此类推,每个位置实体取一个)。正如您所看到的,没有执行任何连接,因此我很惊讶地听到您的经验是不同的。
关于您希望缓存应该如何工作的描述,这就是当类被固定时,第2级缓存应该如何工作。事实上,我甚至不会在应用程序启动时尝试将位置对象预加载到缓存中。让DN累积地缓存它们。
的确,如果采用JDO,您可能不得不接受一些妥协……您将不得不放弃基于JDBC的手动DAO获得的绝对控制权。但在这种情况下,至少你应该能够实现你想要的。它确实是2级缓存的原型用例之一。
https://stackoverflow.com/questions/3224584
复制相似问题