From e10a184f20875f1178a0505c6f16d3cbedcbf0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E4=BF=8A=E5=BD=A6?= <1101703918@qq.com> Date: Thu, 17 Apr 2025 14:55:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E9=87=8Asrc/main/java/net/micode/note?= =?UTF-8?q?s/gtask/data/SqlData.java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/micode/notes/gtask/data/SqlData.java | 128 +++++++++++++----- 1 file changed, 95 insertions(+), 33 deletions(-) diff --git a/src/main/java/net/micode/notes/gtask/data/SqlData.java b/src/main/java/net/micode/notes/gtask/data/SqlData.java index d3ec3be..41b9648 100644 --- a/src/main/java/net/micode/notes/gtask/data/SqlData.java +++ b/src/main/java/net/micode/notes/gtask/data/SqlData.java @@ -34,54 +34,65 @@ import net.micode.notes.gtask.exception.ActionFailureException; import org.json.JSONException; import org.json.JSONObject; - +/** + * 数据库数据操作封装类 + * 负责处理与便笺数据表(Data表)的CRUD操作 + * 封装数据字段的差异更新逻辑,支持事务性提交 + */ public class SqlData { + // 日志标签 private static final String TAG = SqlData.class.getSimpleName(); + // 无效ID标识(用于新建条目) private static final int INVALID_ID = -99999; + // 数据表查询字段投影(对应DATA表的列) public static final String[] PROJECTION_DATA = new String[] { - DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, - DataColumns.DATA3 + DataColumns.ID, // 0:数据ID + DataColumns.MIME_TYPE, // 1:MIME类型 + DataColumns.CONTENT, // 2:内容主体(JSON格式) + DataColumns.DATA1, // 3:扩展数据1(长整型) + DataColumns.DATA3 // 4:扩展数据3(字符串) }; + // 投影字段索引常量 public static final int DATA_ID_COLUMN = 0; - public static final int DATA_MIME_TYPE_COLUMN = 1; - public static final int DATA_CONTENT_COLUMN = 2; - public static final int DATA_CONTENT_DATA_1_COLUMN = 3; - public static final int DATA_CONTENT_DATA_3_COLUMN = 4; - private ContentResolver mContentResolver; - - private boolean mIsCreate; - - private long mDataId; - - private String mDataMimeType; - - private String mDataContent; - - private long mDataContentData1; - - private String mDataContentData3; - - private ContentValues mDiffDataValues; - + // 成员变量 + private ContentResolver mContentResolver; // 内容解析器(用于数据库操作) + private boolean mIsCreate; // 新建数据条目标识 + private long mDataId; // 当前数据ID + private String mDataMimeType; // MIME类型(默认NOTE类型) + private String mDataContent; // 内容主体 + private long mDataContentData1; // 扩展数据1(长整型字段) + private String mDataContentData3; // 扩展数据3(字符串字段) + private ContentValues mDiffDataValues; // 差异字段集合(用于批量更新) + + /** + * 新建数据构造函数 + * @param context 上下文对象(用于获取ContentResolver) + */ public SqlData(Context context) { mContentResolver = context.getContentResolver(); mIsCreate = true; + // 初始化默认值 mDataId = INVALID_ID; - mDataMimeType = DataConstants.NOTE; + mDataMimeType = DataConstants.NOTE; // 默认MIME类型为便笺 mDataContent = ""; mDataContentData1 = 0; mDataContentData3 = ""; mDiffDataValues = new ContentValues(); } + /** + * 从数据库游标构造(用于加载已有数据) + * @param context 上下文对象 + * @param c 数据库查询结果游标 + */ public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; @@ -89,6 +100,10 @@ public class SqlData { mDiffDataValues = new ContentValues(); } + /** + * 从游标加载数据到成员变量 + * @param c 已定位到正确位置的数据库游标 + */ private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); @@ -97,13 +112,21 @@ public class SqlData { mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } + /** + * 通过JSON对象设置数据内容 + * @param js 包含数据字段的JSON对象 + * @throws JSONException JSON解析异常 + * 说明:自动记录变化的字段,用于后续差异更新 + */ public void setContent(JSONObject js) throws JSONException { + // 处理数据ID字段 long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; + // 处理MIME类型字段 String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { @@ -111,18 +134,21 @@ public class SqlData { } mDataMimeType = dataMimeType; + // 处理内容主体字段 String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); } mDataContent = dataContent; + // 处理扩展数据1(长整型) long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); } mDataContentData1 = dataContentData1; + // 处理扩展数据3(字符串) String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); @@ -130,6 +156,12 @@ public class SqlData { mDataContentData3 = dataContentData3; } + /** + * 获取当前数据的JSON表示 + * @return 包含所有数据字段的JSON对象 + * @throws JSONException JSON构造异常 + * 注意:新建对象(未提交)调用此方法将返回null + */ public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); @@ -144,34 +176,59 @@ public class SqlData { return js; } + /** + * 提交数据变更到数据库 + * @param noteId 关联的便笺ID + * @param validateVersion 是否进行版本验证 + * @param version 当前数据版本(用于乐观锁) + * @throws ActionFailureException 数据库操作失败时抛出 + * + * 执行逻辑: + * 1. 新建模式:插入新记录并获取分配的ID + * 2. 更新模式:根据差异字段进行更新 + * - 带版本验证的更新可防止同步冲突 + */ public void commit(long noteId, boolean validateVersion, long version) { - if (mIsCreate) { + // 新建数据处理 if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { - mDiffDataValues.remove(DataColumns.ID); + mDiffDataValues.remove(DataColumns.ID); // 移除无效ID } + // 添加关联便笺ID mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + + // 执行插入操作 Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); try { + // 从返回URI中解析新分配的ID mDataId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } } else { + // 更新数据处理 if (mDiffDataValues.size() > 0) { int result = 0; if (!validateVersion) { - result = mContentResolver.update(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); + // 普通更新 + result = mContentResolver.update( + ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, mDataId), + mDiffDataValues, + null, + null + ); } else { - result = mContentResolver.update(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, + // 带版本验证的更新(防止同步冲突) + result = mContentResolver.update( + ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, mDataId), + mDiffDataValues, + // 使用子查询确保版本匹配 " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE - + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { - String.valueOf(noteId), String.valueOf(version) - }); + + " WHERE " + NoteColumns.VERSION + "=?)", + new String[] { String.valueOf(noteId), String.valueOf(version) } + ); } if (result == 0) { Log.w(TAG, "there is no update. maybe user updates note when syncing"); @@ -179,10 +236,15 @@ public class SqlData { } } + // 重置状态 mDiffDataValues.clear(); mIsCreate = false; } + /** + * 获取当前数据ID + * @return 有效数据ID,新建对象提交前返回INVALID_ID + */ public long getId() { return mDataId; }