您的位置:首页 > 移动开发 > Android开发

Android中使用ON CONFLICT REPLACE同步数据到SQLITE

2016-08-29 21:03 393 查看
最近我开发的一个功能需要从服务端获取json同步到本地的sqlite数据库,然后通知UI更新(Sqlbrite ftw)。这块的数据有一个字段叫isRead,当它为true的时候表示用户在UI上删除了它,app不再显示那条数据。

数据模型是这样的:

[java] view
plain copy

 





public class Alert {    

    private final String alertId;  

    private final String alertTitle;  

    private final String alertText;  

    private final String alertActionText;  

    private final String alertActionUrl;  

    private final Boolean isRead;  

   

    ...  

   

}  

而数据库是这样的:

[java] view
plain copy

 





private static final String TABLE_CREATE_ALERTS = "CREATE TABLE "    

        + ALERTS + "("   

        + ALERT_ID + " text UNIQUEE, "   

        + ALERT_TITLE + " text, "   

        + ALERT_TEXT + " text, "   

        + ALERT_ACTION_TEXT + " text, "   

        + ALERT_ACTION_URL + " text, "   

        + ALERT_IS_READ + " integer default 0);";  

当我们相服务器发起请求之后,同步返回的Alert集合应该是这样的:

[java] view
plain copy

 





@Override  

public Observable<Alert> syncAlerts(final List<Alert> alerts) {  

   

    Observable<Alert> observable = Observable.from(alerts)  

        .doOnNext(new Action1<Alert>() {  

            @Override  

            public void call(Alert alert) {  

   

                Cursor doesExistCursor = null;  

                BriteDatabase.Transaction transaction = db.newTransaction();  

                try {  

                    doesExistCursor = db.query(AlertQueries.byAlertIdQuery(alert.getAlertId()));  

   

                    //then the row exists and we shouldn't insert  

                    if (doesExistCursor != null && doesExistCursor.moveToNext()) {  

   

                        return;  

                    }  

   

                    ContentValues values = new AlertsContentValuesBuilder()  

                            .withAlertId(alert.getAlertId())  

                            .withAlertTitle(alert.getAlertTitle())  

                            .withAlertText(alert.getAlertText())  

                            .withActionText(alert.getActionText())  

                            .withActionUrl(alert.getActionUrl())  

                            .build();  

   

                    db.insert(MyOpenHelper.TABLE_ALERTS, values, SQLiteDatabase.CONFLICT_IGNORE);  

                    transaction.markSuccessful();  

                } finally {  

                    transaction.end();  

                    if (doesExistCursor != null) {  

                        doesExistCursor.close();  

                    }  

                }  

   

            }  

        });  

    return observable;  

}  

大体来说,这段代码查看了该Alert是否已经存在于本地数据库中,如果不是,插入之。

但是这段代码有两个缺点。

我们跑了两次数据库(查询和插入)

如果Alert已经存在,我们不会更新各个字段,但是很可能上次查询的数据已经和之前不一样了。


ON CONFLICT REPLACE

解决这两个问题的方法就是在alertId上添加ON CONFLICT REPLACE

[java] view
plain copy

 





private static final String TABLE_CREATE_ALERTS = "CREATE TABLE "    

        + ALERTS + "("   

        + ALERT_ID + " text UNIQUE ON CONFLICT REPLACE, "   

        + ALERT_TITLE + " text, "   

        + ALERT_TEXT + " text, "   

        + ALERT_ACTION_TEXT + " text, "   

        + ALERT_ACTION_URL + " text, "   

        + ALERT_IS_READ + " integer default 0);";  

然后创建一个query来使用它

[java] view
plain copy

 





private static final String ALERT_INSERT_OR_REPLACE = "INSERT OR REPLACE INTO " +    

        MyOpenHelper.TABLE_ALERTS +" ( "  +  

        MyOpenHelper.ALERT_ID + " , " +  

        MyOpenHelper.ALERT_TITLE + " , " +  

        MyOpenHelper.ALERT_TEXT + " , " +  

        MyOpenHelper.ALERT_ACTION_TEXT + " , " +  

        MyOpenHelper.ALERT_ACTION_URL + " , " +  

        MyOpenHelper.ALERT_IS_READ +  ") VALUES (?, ?, ?, ?, ?, COALESCE((SELECT " +  

        MyOpenHelper.ALERT_IS_READ + " FROM " +  

        MyOpenHelper.TABLE_ALERTS + " WHERE " +  

        MyOpenHelper.ALERT_ID + " = ?), 0));";  

有点难懂,其实做了如下事情

我们使用INSERT OR REPLACE告诉数据库,如果插入有冲突则用新的值替换。

我们还确保了在同步的时候isRead字段不会被覆盖。我们是这样做的:查询字段当前值,如果不存在则设置一个默认的值0.

现在syncAlerts() 是这样的

[java] view
plain copy

 





@Override  

public void syncAlerts(final List<Alert> alerts) {  

   

    for (Alert alert: alerts) {  

        BriteDatabase.Transaction transaction = db.newTransaction();  

        try {  

            db.executeAndTrigger(MyOpenHelper.TABLE_ALERTS,   

                ALERT_INSERT_OR_REPLACE,   

                alert.getAlertId(),   

                alert.getAlertTitle(),   

                alert.getAlertText(),   

                alert.getActionText(),   

                alert.getActionUrl(),   

                alert.getAlertId());  

            transaction.markSuccessful();  

        } catch (Exception e) {  

            Logger.errorNotify(e);  

        } finally {  

            transaction.end();  

        }  

    }  

}  

最后要注意的就是你该调用 BriteDatabase.executeAndTrigger(...)而不是BriteDatabase.execute(...)。

 就这样稍微写了个复杂点的插入语句,我们就能利用Sqlite的INSERT OR REPLACE INTO 极大的简化我们的代码。
转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0503/4202.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: