首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >避免加入DataNucleus?

避免加入DataNucleus?
EN

Stack Overflow用户
提问于 2010-07-11 20:52:07
回答 1查看 1.8K关注 0票数 2

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

假设我有一些类如下所示:

公共类位置{私有整数id;私有字符串标题;}

公共类雇员{私有整数id;私有字符串名称;私有职位位置;}

位置SQL表的内容实际上并不经常更改。使用JDBC,我将整个表读入内存(具有定期或随意刷新的能力)。然后,当我将雇员读入内存时,我只需从Employee表中检索位置ID,并使用该ID获取内存中的位置实例。

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

代码语言:javascript
复制
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,遍历所有员工,获得他们的职位:

代码语言:javascript
复制
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避免加入吗?

代码语言:javascript
复制
<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。

代码语言:javascript
复制
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无法进行这种优化。是那么回事吗?没关系,只是不像我想的那样。

EN

回答 1

Stack Overflow用户

发布于 2010-07-12 06:51:03

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

此外,可以使用二级缓存避免第二次获取。首先检查是否实际启用了2级缓存(在DataNucleus 1.1中,默认情况下禁用它,在2.0中默认启用它)。然后,您可能应该“锁定”类,以便无限期地缓存它的位置实体:

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

编辑以添加:

元数据中的标记不适合这种情况。实际上,您根本不需要显式地指定关系,DataNucleus将从类型中找出它。但是,当您说需要在默认的fetch组中读取POSITION_ID时,您是正确的。所有这些都可以通过对元数据的以下更改来实现:

代码语言:javascript
复制
<field name="position" default-fetch-group="true">
    <column name="POSITION_ID" jdbc-type="int" />
</field>

编辑以添加:

为了澄清,在上面删除了元数据更改之后,我运行了您提供的测试代码(由一个MySQL数据库支持),我只看到了这两个查询:

代码语言:javascript
复制
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()方法以返回职位的标题,然后运行示例代码的第二部分,那么对数据库的调用如下:

代码语言:javascript
复制
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级缓存的原型用例之一。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3224584

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档