6

私は Android プロジェクトに取り組んでおり、現在、オブジェクト グラフへの参照サイクルを含む sJSONからいくつかを逆シリアル化し、それを操作してデータベースに保存する方法を見つけようとしています。例を挙げましょう:API

{
    "id": "24",
    "name": "Bob",
    "friends": [
        {
            "id": "13",
            "name": "Alice",
            "friends": [
                {
               "id": "24" // and we have a circular reference
                }
            ]
        }
    ]
}

ここで、 と呼ばれる person オブジェクトは personBobと友達でAliceあり、さらに とAlice友達Bobです。この関係は再帰的であるため、 とAliceの友人関係Bobは完全な人物オブジェクトとして認識されなくなり、彼の友人のみidが提供されます。

上記の手順を実行するためにどのツールを使用しますか? Jackson でオブジェクト マッピング部分を実装しようとしましたが、サイクル要件の解決策を見つけることができませんでした。このトピックについて進行中の議論でJSOGについて言及されているのを見つけました。これは参考になるかもしれませんが、私たちの API は固定されており、JSOG に準拠していません。

基本的に私が探しているのは、Android 用の RestKit (iOS フレームワーク) のようなものです。

4

3 に答える 3

4

API が修正されたら、次のように実装します。

DB の観点からは、友達グラフのすべてのエッジを保持するために、UserTableRelationsTableの 2 つのテーブルが必要です。
ここに画像の説明を入力

つまり、ユーザーを 1 つのテーブルに保持し、その関係を関係テーブルに保持するという考え方です。後でその上に追加のロジックを追加することもできます (たとえば、ユーザーが自分の接続を非表示にするか、誰かをブロックするなど - グラフの可能なエッジ)。また、循環参照の問題を軽減することもできます

サービスからデータを取得し、JSON を解析するためのフレームワークとして、私は Retrofit を使用します

まず、次のクラスを定義UserBaseします。User

public class UserBase {
    public string id;
}

public final class User extends UserBase {
    public string name;
    public List<UserBase> friends;
    // user's "real" friends, not just ids, fills from SQLite
    public List<User> userFriends;
}

ご覧のとおり、friendsは、Retrofitが JSON からオブジェクトを解析するためのオブジェクトUserBaseのリストです。このリストは、以降の手順で手動で SQLite から入力します。userFriends

それでは、DB で動作するいくつかのヘルプ クラスを定義しましょう。

public interface Dao<TItem> {
    void add(List<TItem> items);
    void removeAll();
    List<TItem> getAll();
}
....
public abstract class AbstractDao<TItem> implements Dao<TItem> {
    protected final SQLiteDatabase database;
    protected final SqlUtilities sqlUtilities;

    public AbstractDao(SQLiteDatabase database, SqlUtilities sqlUtilities) {
        this.database = database;
        this.sqlUtilities = sqlUtilities;
    }
}

ここで、RelatedTable と UserTable に Dao が必要です。

public class UserRelation {
    public String mainUserId;
    public String relatedUserId;
}
...
public interface UserRelationDao extends Dao<UserRelation> {
    ...
    List<User> getFriends(String userId);
    ...
}
... 
public interface UserDao extends Dao<User> {
    ...
    void addWithIgnore(List<TItem> items);
    void update(List<TItem> items);
    void upsert(List<TItem> items);

    User getById(String userId);
    ...
}

完了したら、実際にこのインターフェースを実装できます。

DefaultUserRelationDaoクラス:

public class DefaultUserRelationDao extends AbstractDao<UserRelation> implements UserRelationDao {
    static final String MAIN_USER_COLUMN = "mainuser";
    static final String RELATED_USER_COLUMN = "relateduser";

    private static final String[] COLUMN_NAMES = new String[]{
            MAIN_USER_COLUMN,
            RELATED_USER_COLUMN,
    };

    private static final String[] COLUMN_TYPES = new String[]{
            "TEXT",
            "TEXT",
    };

    private static final String TABLE = "userrelation";
    static final String CREATE_TABLE = SqlUtilities.getCreateStatement(TABLE, COLUMN_NAMES, COLUMN_TYPES);
    static final String ALL_CONNECTED_USERS =
            "SELECT " + Joiner.on(",").join(DefaultUserDao.COLUMN_NAMES) +
                    " FROM " + UserTable.TABLE_NAME + "," + TABLE +
                    " WHERE " + RELATED_USER_COLUMN + "=" + DefaultUserDao.USER_ID_COLUMN;

    public DefaultUserRelationDao(SQLiteDatabase database, SqlUtilities sqlUtilities) {
        super(database, sqlUtilities);
    }

    @Override
    public void add(List<UserRelation> userRelations) {
        try {
            database.beginTransaction();
            ContentValues contentValues = new ContentValues();

            for (UserRelation relation : userRelations) {
                sqlUtilities.setValuesForUsersRelation(contentValues, relation);
                database.insertOrThrow(TABLE, null, contentValues);
            }

            database.setTransactionSuccessful();
        } finally {
            database.endTransaction();
        }
    }

    @Override
    public List<User> getFriends(String userId) {
        Cursor cursor = database.rawQuery(ALL_CONNECTED_USERS, new String[]{userId});
        return sqlUtilities.getConnectedUsers(cursor);
    }
}

およびDefaultUserDaoクラス:

public final class DefaultUserDao extends AbstractUDao<User> implements UserDao {

    public static final String USER_ID_COLUMN = "userid";
    static final String USER_NAME_COLUMN = "username";

    public static final String[] COLUMN_NAMES = new String[]{
            USER_ID_COLUMN,
            USER_NAME_COLUMN,
    };

    private static final String TABLE = "users";
    private static final String SELECT_BY_ID =
            SqlUtilities.getSelectWhereStatement(TABLE, COLUMN_NAMES, new String[]{ USER_ID_COLUMN });

    static final String CREATE_TABLE = SqlUtilities.getCreateStatement(TABLE, COLUMN_NAMES, COLUMN_TYPES);

    public DefaultUserDao(SQLiteDatabase database, SqlUtilities sqlUtilities) {
        super(database, sqlUtilities);
    }

    @Override
    public void add(List<User> users) {
        try {
            database.beginTransaction();
            ContentValues contentValues = new ContentValues();

            for (User user : users) {
                sqlUtilities.setValuesForUser(contentValues, user);
                database.insertOrThrow(UserTable.TABLE_NAME, null, contentValues);
            }

            database.setTransactionSuccessful();
        } finally {
            database.endTransaction();
        }
    }

    @Override
    public User getById(String userId) {
        return getUserBySingleColumn(SELECT_BY_ID, userId);
    }
    .....
    private User getUserBySingleColumn(String selectStatement, String value) {
        Cursor cursor = database.rawQuery(selectStatement, new String[]{value});
        List<User> users = sqlUtilities.getUsers(cursor);
        return (users.size() != 0) ? users.get(0) : null;
    }
}

テーブルを作成するには、拡張SQLiteOpenHelperしてonCreate()実際にテーブルを作成する必要があります。

public final class DatabaseHelper extends SQLiteOpenHelper {
    static final String DATABASE_NAME = "mysuper.db";
    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, SCHEMA_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DefaultUserDao.CREATE_TABLE);
        db.execSQL(DefaultUserRelationDao.CREATE_TABLE);
    }
    ...
}

ここで、キャッシュで可能なすべてのアクションで LocalStorage インターフェイスを定義することをお勧めします。

  • すべてのユーザーを取得する
  • IDでユーザーを取得
  • ユーザーを追加
  • ユーザー間のつながりを追加する
  • public interface LocalStorage {
       User getUserById(String userId);
       void addUsers(List<User> users);
       ....
    }
    

そしてそれは実装です:

public final class SqlLocalStorage implements LocalStorage {

    private UserDao userDao;
    private UserRelationDao userRelationDao;

    private SQLiteDatabase database;
    private final Object initializeLock = new Object();
    private volatile boolean isInitialized = false;
    private SqlUtilities sqlUtilities;

    // there database is
    //    SQLiteOpenHelper helper = new DatabaseHelper(context);
    //    database = helper.getWritableDatabase();
    public SqlLocalStorage(SQLiteDatabase database, SqlUtilities sqlUtilities) {
        this.database = database;
        this.sqlUtilities = sqlUtilities;
    }

    @Override
    public User getUserById(String userId) {
        initialize();

        User user = userDao.getById(userId);
        if (user == null) {
            return null;
        }

        List<User> relatedUsers = userRelationDao.getFriends(userId);
        user.userFriends = relaterUsers;
        return user;
    }

    @Override
    public void addUsers(List<User> users) {
        initialize();

        for (User user : users) {
            for (UserBase friend : user) {
                UserRelation userRelation = new UserRelation();
                userRelation.mainUserId = user.id;
                userRelation.relatedUserId = friend.id;

                UserRelation userRelationMutual = new UserRelation();
                userRelationMutual.mainUserId = friend.id;
                userRelationMutual.relatedUserId = user.id;

                userRelationDao.add(userRelation);
                userRelationMutual.add(userRelation)
            }
        }

        userDao.addWithIgnore(users);
    }

    void initialize() {
        if (isInitialized) {
            return;
        }

        synchronized (initializeLock) {
            if (isInitialized) {
                return;
            }

            Log.d(LOG_TAG, "Opens database");
            userDao = new DefaultUserDao(database, sqlUtilities);
            userRelationDao = new DefaultUserRelationDao(database, sqlUtilities);
            isInitialized = true;
        }
    }
}

最後のステップ - 実際の使用法:

//somewhere in non-UI thread
List<User> users = dataSource.getUsers();
localStorage.addUsers(users);
final User userBob = localStorage.getUserById("42");

注意!ここでは、カスタム クラス SqlUtilities を多用しています。残念ながら、ここに投稿するには大きすぎますが、内容を示すための例にすぎません。 getUsers(Cursor cursor) の外観は次のとおりです。

.....
public List<User> getUsers(Cursor cursor) {
    ArrayList<User> users = new ArrayList<>();
    try {
        while (cursor.moveToNext()) {
            users.add(getUser(cursor));
        }
    } finally {
        cursor.close();
    }

    return users;
}

private User getUser(Cursor cursor) {
    User user = new User(cursor.getString(0));
    user.FullName = cursor.getString(1);
    ....
    return user; 
}
.....

一部の詳細をスキップすることをお許しください(特に、DB を更新する必要がある場合、データがいっぱいでなく、キャッシュから取得する以外に、サーバーから取得してからロードする必要がある場合)キャッシュなど)。重要な部分が欠落している場合は、コメント欄に投稿してください。喜んで投稿を更新します。

お役に立てば幸いです。

于 2016-01-24T09:30:37.730 に答える
1

JSON-RPCを調べることができます。これは、複雑なオブジェクト関係の JSON 解析とオブジェクト マッピングをサポートする優れたフレームワークです。

于 2016-01-26T19:48:54.363 に答える