質問

Im using GreenDAO and Volley. So I have the following problem: When I make a network request I need to parse with GSON so I have a model to represent entities retrieved from server and other model to represent the GreenDAO objects. Is there any way to only have 1 class per model to represent as a GSON and a Class of ORM?

class Product:

@SerializedName("id")
private String id;

@SerializedName("pictures")
private List<Picture> pictures;

get & set

class PersistentProduct:

private Long id;
private List<Picture> pictures;

/** To-many relationship, resolved on first access (and after reset). Changes to to-many relations are not persisted, make changes to the target entity. */
public List<PersistencePicture> getPictures() {
    if (pictures == null) {
        if (daoSession == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        PersistencePictureDao targetDao = daoSession.getPersistencePictureDao();
        List<PersistencePicture> picturesNew = targetDao._queryPersistenceProduct_Pictures(id);
        synchronized (this) {
            if(pictures == null) {
                pictures = picturesNew;
            }
        }
    }
    return pictures;
}

First I thought to make a Interface, but when you retrieve the data from a DAO the DAO returns the class and not the interface, so I think cannot do in this way, the only solution I found is to make a "ProductUtils" that converts from a "PersistentProduct" to a "Product" and vice versa.

役に立ちましたか?

解決

The most elegant way would be to implement a small extension for greendao, so that you can specify the serialized name during schema-creation.

For Example:

de.greenrobot.daogenerator.Property.java:

// in PropertyBuilder append these lines
public PropertyBuilder setSerializedName(String sname) {
    // Check the sname on correctness (i.e. not empty, not containing illegal characters)
    property.serializedName = sname;
    return this;
}

// in Property append these lines
private String serializedName = null;

public boolean isSerialized() {
    return serializedName != null;
}

In entity.ftl add this line after line 24 (after package ${entity.javaPackage};):

<#if property.serializedName??>
import com.google.gson.annotations.SerializedName;
</#if>

And after line 55 (after: <#list entity.properties as property>)

<#if property.serializedName??>
@SerializedName("${property.serializedName}")
</#if>

Afterwards you should be able to use you generated greendao-entity for volley with the following restrictions:

  1. If you get a Product over network, nothing is changed in the db, yet. You have to call insertOrReplace().
  2. If you get a Product from db and send it via network some undesired fields might be serialized (i.e. myDao and daoSession)
  3. If you get a Product via network and call insertOrReplace() the "network"-Product will be persisted and a already existing Product will be replaced by it BUT the referenced entities won't get updated or persisted if insertOrReplace() isn't called for each of them!
  4. If you get a Product via network and call insertOrReplace() for every referenced entity toMany-entities that were referenced by the db-Product are still referenced by the updated Product, although they are not listed in the updated Product. You have to call resetPictures() and getPictures() to get the correct list, which will contain all toMany()-entities references by either the original Product stored in DB or the updated Product from network.

Update addressing 2.

To prevent daoSession and myDao from being serialized, you can use the following ExclusionStrategy:

private static class TransientExclusionStrategy implements ExclusionStrategy {
    public boolean shouldSkipClass(Class<?> clazz) {
        return (clazz.getModifiers() & java.lang.reflect.Modifier.TRANSIENT) != 0;
    }

    public boolean shouldSkipField(FieldAttributes f) {
        return f.hasModifier(java.lang.reflect.Modifier.TRANSIENT);
    }
}

Update addressing 1.,3. and 4.

As a fast solution you can add the following method in the KEEP-SECTIONS of your entity:

public void merge(DaoSession s) {
    s.insertOrReplace(this);

    // do this for all toMany-relations accordingly
    for (Picture p : getPictures()) {
        s.insertOrReplace(p);
        newPics.add(p.getId());
    }
    resetPictures();
}

This will result in the original entity being updated and attached to the session and dao. Also every Picture that is references by the network-product will be persisted or updated. Pictures reference by the original entity, but not by the network-entity remain untouched and get merged into the list.

This is far from perfect, but it shows where to go and what to do. The next steps would be to do everything that is done in merge() inside one transaction and then to integrate different merge-methods into dao.ftl.

NOTE The code given in this answer is neither complete nor tested and is meant as a hint on how to solve this. As pointed out above this solution still has some restrictions, that have to be dealt with.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top