一对多关系和序列化对象字段

时间:2013-09-30 07:36:26

标签: jdo datanucleus

我有以下与1对N关系的Persistable类。

@PersistenceCapable
public class Pet {

  @Persistent(primaryKey = "true", valueStrategy = IdGeneratorStrategy.IDENTITY)
  Long id;

  @Persistent
  String name;

  @Element(column = "PET_ID")
  List<Photo> photos;

  // getters and setters

@PersistenceCapable
public class Photo {

  @Persistent(primaryKey = "true", valueStrategy = IdGeneratorStrategy.IDENTITY)
  Long id;

  @Persistent
  String desc;

  @Persistent(serialized="true")
  Object image;

  // getters and setters

  // hash and equal using field id

字段列出照片使用FK在Pet(1)和Photo(N)之间建立1-N关系。 Photo中的字段对象图像是序列化的,用于保存图像对象。

对于数据存储区操作,我使用PetDao,它有以下方法

public final static PersistenceManagerFactory pmf = JDOHelper
            .getPersistenceManagerFactory("datastore");

public void storePet(Pet pet) {
    // get PM and current tx
    try {
       tx.begin();
       pm.makePersistent(pet);
       tx.commit();
    } catch (Exception e) {
       // rollback and close pm
    }           
}

public void storePhoto(Long petId, Photo photo) {
    // get PM and current tx
    try {
       tx.begin();
       Pet pet = pm.getObjectById(Pet.class,petId);
       pet.addPhoto(photo);
       tx.commit();
    } catch (Exception e) {
       // rollback and close pm
    }
}

我创建并持久化对象

Pet pet = new Pet();
pet.setName("Nicky");

Photo photo = new Photo();
photo.setDesc("Photo 1");
photo.setImage(new Image("image 1"));
pet.addPhoto(photo);

.... add photo 2 and photo 3

PetDao petDao = new PetDao();       
petDao.storePet(pet);

// i have one more photo so add it directly
photo = new Photo();
photo.setDesc("Photo 4");
photo.setImage(new Image ("image 4"));      

petDao.storePhoto((long)0, photo);

根据需要,一切都会持续存在,数据存储区最终会在PET桌上放一张宠物,在PHOTO桌上放一张4张照片。

但是,当我分析DataNucleus日志中的 petDao.storePhoto((长)0,照片)代码时,我看到DataNucleus从数据存储中检索所有图像对象。

Native          [DEBUG] INSERT INTO PHOTO ("DESC",IMAGE,PET_ID,PHOTOS_INTEGER_IDX) VALUES (<'Photo 4'>,<UNPRINTABLE>,<0>,<3>)
Persist         [DEBUG] Execution Time = 70 ms (number of rows = 1) on PreparedStatement "org.datanucleus.store.rdbms.ParamLoggingPreparedStatement@190a0d6"
Persist         [DEBUG] Object "in.m.pet.Photo@10deb5f" was inserted in the datastore and was given strategy value of "3"
Native          [DEBUG] SELECT A0.IMAGE FROM PHOTO A0 WHERE A0.ID = <1>
Retrieve        [DEBUG] Execution Time = 1 ms
Native          [DEBUG] SELECT A0.IMAGE FROM PHOTO A0 WHERE A0.ID = <0>
Retrieve        [DEBUG] Execution Time = 0 ms
Native          [DEBUG] SELECT A0.IMAGE FROM PHOTO A0 WHERE A0.ID = <2>
Retrieve        [DEBUG] Execution Time = 0 ms

使用INSERT INTO PHOTO ...语句添加“Photo 4”后,DataNucleus通过触发3 SELECT IMAGE FROM PHOTO语句来检索前面的三个图像对象。 随着图像对象数量的增加,这些检索可能会非常大,从而导致数据存储上不必要的负载,从而影响性能。

如果我使用pm.getObjectById()选择宠物并分离Pet对象并将照片添加到分离的对象,然后使用pm.makePersistent(pet)将其附加回对象图,则会发生同样的事情。 FetchGroup如下

@PersistenceCapable(detachable="true")
@FetchGroup(name="detachPhotos", members={@Persistent(name="photos")})
public class Pet {
   ....
}

并使用fetchgroup分离宠物

public Pet getPet(Long id){
    PersistenceManager pm = pmf.getPersistenceManager();
    pm.getFetchPlan().addGroup("detachPhotos");
    Pet pet = pm.getObjectById(Pet.class, id);      
    return pm.detachCopy(pet);  
}

我的问题是如何避免数据存储区中对象图像的这些不必要的重试。

还有一个观察:如果我从另一个应用程序调用petDao.storePhoto((long)0,photo)或在PetDao.storePhoto方法中使用单独的PMF实例,那么DataNucleus 将不要激活SELECT来检索图像对象。

2 个答案:

答案 0 :(得分:1)

如果1-N关系变大,您可能需要考虑相关地映射,即不是映射Pet.photos,而是映射Photo.pet。这将阻止您在没有查询的情况下以OO方式从Pet导航到Photo,但会阻止您关注的SQL语句。

您的storePhoto将如下所示,并且不会获取1-N。

public void storePhoto(Photo photo) {
    // get PM and current tx
    try {
       tx.begin();
       pm.makePersistent(photo); // assuming pet was already set
       tx.commit();
    } catch (Exception e) {
       // rollback and close pm
    }
}

答案 1 :(得分:0)

DataNucleus Performance Tuning中得到答案,如果应用不需要覆盖率,则建议设置 datanucleus.persistenceByReachabilityAtCommit = false 。将其设置为false,可解决图像检索问题,而不会产生任何其他副作用。

引用DN doc

  

DataNucleus验证新持久化对象是否可以访问内存   在提交时,如果不是,则从数据库中删除它们。这个   进程镜像垃圾收集,其中对象未被引用   垃圾收集或从内存中删除。可达性是   昂贵,因为它遍历整个对象树,可能需要   从数据库重新加载数据。如果您不需要可达性   应用程序,你应该禁用它。

在将其设置为false之前,请检查您的应用是否需要覆盖率

相关问题