You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xiaomi-Notes/NotesDatabaseHelper.java

463 lines
22 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.data;
//引入必要的类
// 用于封装一组键值对
import android.content.ContentValues;
// 上下文类
import android.content.Context;
// SQLite数据库类
import android.database.sqlite.SQLiteDatabase;
// SQLite数据库助手类
import android.database.sqlite.SQLiteOpenHelper;
// 日志类
import android.util.Log;
// 导入数据列相关常量
import net.micode.notes.data.Notes.DataColumns;
// 导入数据相关常量
import net.micode.notes.data.Notes.DataConstants;
// 导入笔记列相关常量
import net.micode.notes.data.Notes.NoteColumns;
//创建 NotesDatabaseHelper 类 ,继承 SQLiteOpenHelper
public class NotesDatabaseHelper extends SQLiteOpenHelper {
// 定义数据库名称,只能在 NotesDatabaseHelper 类内部访问,并且在程序运行过程中无法修改。
private static final String DB_NAME = "note.db";
// 数据库版本
private static final int DB_VERSION = 4;
// 定义表名 TABLE 接口
public interface TABLE {
// 笔记表名
public static final String NOTE = "note";
// 数据表名
public static final String DATA = "data";
}
//日志标签
private static final String TAG = "NotesDatabaseHelper";
// 单例模式实例
private static NotesDatabaseHelper mInstance;
// 在数据库创建笔记表
private static final String CREATE_NOTE_TABLE_SQL =
"CREATE TABLE " + TABLE.NOTE + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," + // 笔记 id主键
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + // 笔记的父级id默认为 0
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + // 警告日期,默认为 0
NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + // 背景颜色 id 默认为0
// 笔记创建日期,默认为当前时间,单位是毫秒
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
// 是否有附件,默认为 0
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +
// 修改日期,默认为当前时间,单位是毫秒
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
// 笔记的数量,默认为 0
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +
// 摘要,默认为空字符串
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + //笔记类型,默认为 0
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + // 小部件 id默认为 0
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + // 小部件类型,默认为 -1
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + // 同步 id 默认为0
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + // 本地修改标识,默认为 0
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + // 原始父级 id ,默认为 0
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + // GTASK ID,默认为空字符串
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + // 版本,默认为 0
")";
// 在数据库创建数据表
private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," + // 数据 id主键
DataColumns.MIME_TYPE + " TEXT NOT NULL," + // MIME 类型,不能为空
DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + // 笔记 id默认为0
// 创建日期,默认为当前时间(毫秒)
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
// 修改日期,默认为当前时间(毫秒)
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + // 内容,默认为空字符串
DataColumns.DATA1 + " INTEGER," + // 自定义数据 1
DataColumns.DATA2 + " INTEGER," + // 自定义数据 2
DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + // 自定义数据 3默认为空字符串
DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + // 自定义数据 4默认为空字符串
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + // 自定义数据 5默认为空字符串
")";
// 创建索引以加速根据 NOTE_ID 查询的数据表
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
"CREATE INDEX IF NOT EXISTS note_id_index ON " +
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";
/**
* Increase folder's note count when move note to the folder
*/
// 增加文件夹中的笔记计数,当笔记移动到该文件夹时
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_update "+
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + // 更新笔记数量
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + // 按照新父级 id 更新
" END"; // 结束触发器
/**
* Decrease folder's note count when move note from folder
*/
// 创建一个触发器,该触发器在更新笔记的父 id 字段后执行
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_update " +
// 在更新父 id 字段时触发
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
// 更新目标笔记的子笔记数量,减少 1
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
// 条件:只在旧的父 id 等于当前笔记 id 时进行更新,且子笔记数量大于 0
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
" END";
/**
* Increase folder's note count when insert new note to the folder
*/
// 创建一个触发器,该触发器在向笔记表插入新笔记后执行
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_insert " +
// 在插入操作后触发
" AFTER INSERT ON " + TABLE.NOTE +
" BEGIN " +
// 更新目标笔记的子笔记数量,增加 1
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
// 条件: 通过新插入笔记的父 id 找到对应的笔记并更新
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";
/**
* Decrease folder's note count when delete note from the folder
*/
// 创建一个触发器,该触发器在从笔记删除笔记后执行
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_delete " +
// 在删除操作后触发
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN " +
// 更新目标笔记的子笔记数量,数量减 1
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
// 条件:当前笔记 id 等于 旧的父 id并且子笔记数量大于 0
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0;" +
" END";
/**
* Update note's content when insert data with type {@link DataConstants#NOTE}
*/
// 创建一个触发器,该触发器在向数据表插入新数据时执行
private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER =
"CREATE TRIGGER update_note_content_on_insert " +
// 在插入操作后触发
" AFTER INSERT ON " + TABLE.DATA +
// 当新插入的数据类型为 NOTE 时触发
" WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
// 更新目标笔记的内容快照为新插入数据的内容
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
// 条件:通过新插入数据的 NOTE_ID 找到对应的笔记进行更新
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Update note's content when data with {@link DataConstants#NOTE} type has changed
*/
// 创建一个触发器,该触发器在更新数据表中的数据时执行
private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER update_note_content_on_update " +
// 在更新操作后触发
" AFTER UPDATE ON " + TABLE.DATA +
// 当旧数据类型为 NOTE 时触发
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
// 更新目标笔记的内容快照为新更新数据的内容
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
// 条件:通过新更新数据的 NOTE_ID 找到对应的笔记进行更新
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Update note's content when data with {@link DataConstants#NOTE} type has deleted
*/
// 创建一个触发器,该触发器在删除数据表中的数据时执行。
private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =
"CREATE TRIGGER update_note_content_on_delete " +
// 在删除操作后触发
" AFTER delete ON " + TABLE.DATA +
// 当旧数据类型为NOTE时触发
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
// 将目标笔记的内容快照更新为空字符串
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=''" +
// 条件通过旧数据的NOTE_ID找到对应的笔记进行更新
" WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Delete datas belong to note which has been deleted
*/
// 创建一个触发器,该触发器在删除笔记时执行。
private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
"CREATE TRIGGER delete_data_on_delete " +
// 在删除操作后触发
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
// 删除与被删除笔记相关联的所有数据
" DELETE FROM " + TABLE.DATA +
" WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
" END";
/**
* Delete notes belong to folder which has been deleted
*/
// 创建一个触发器,当删除笔记时执行
private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
"CREATE TRIGGER folder_delete_notes_on_delete " +
// 在删除操作后触发
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
// 在删除操作后触发
" DELETE FROM " + TABLE.NOTE +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
/**
* Move notes belong to folder which has been moved to trash folder
*/
// 创建一个触发器,用于移动到垃圾箱文件夹的笔记
private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
"CREATE TRIGGER folder_move_notes_on_trash " +
" AFTER UPDATE ON " + TABLE.NOTE + // 在更新操作后触发
// 当新父ID为垃圾箱文件夹时触发
" WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" BEGIN" +
" UPDATE " + TABLE.NOTE + // 更新所有子笔记的父ID为垃圾箱文件夹ID
" SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
// NotesDatabaseHelper类的构造函数初始化数据库
public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
// 创建笔记表
public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL); // 执行创建笔记表的SQL语句
reCreateNoteTableTriggers(db); // 重新创建与笔记表相关的触发器
createSystemFolder(db); // 创建系统文件夹
Log.d(TAG, "note table has been created"); // 打印日志,指示笔记表已创建
}
// 重新创建笔记表的触发器
private void reCreateNoteTableTriggers(SQLiteDatabase db) {
// 删除已存在的触发器,防止重复创建
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");
// 创建新的触发器
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER);
db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER);
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER);
db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER);
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
}
// 创建系统文件夹
private void createSystemFolder(SQLiteDatabase db) {
ContentValues values = new ContentValues();
/**
* call record foler for call notes
*/
//创建通话记录文件夹用于存储通话笔记
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); // 设置文件夹ID
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); // 设置文件夹类型为系统
db.insert(TABLE.NOTE, null, values); // 插入文件夹记录
/**
* root folder which is default folder
*/
// 创建根文件夹,作为默认文件夹
values.clear(); // 清空上次插入的内容
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); // 设置根文件夹ID
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); // 设置文件夹类型为系统
db.insert(TABLE.NOTE, null, values); // 插入根文件夹记录
/**
* temporary folder which is used for moving note
*/
// 创建临时文件夹,用于移动笔记
values.clear(); // 清空内容
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); // 设置临时文件夹ID
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); // 设置文件夹类型为系统
db.insert(TABLE.NOTE, null, values); // 插入临时文件夹记录
/**
* create trash folder
*/
// 创建垃圾箱文件夹
values.clear(); // 清空内容
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); //设置垃圾箱文件夹ID
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); //设置文件夹类型为系统
db.insert(TABLE.NOTE, null, values); // 插入垃圾箱文件夹记录
}
//创建数据表的方法
public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL); // 执行创建数据表的SQL语句
reCreateDataTableTriggers(db); // 重新创建数据表的触发器
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); // 创建数据表的索引
Log.d(TAG, "data table has been created"); // 打印日志,指示数据表已创建
}
// 重新创建数据表的触发器的方法
private void reCreateDataTableTriggers(SQLiteDatabase db) {
// 如果触发器存在,则删除触发器
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete");
// 创建新的插入触发器
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER);
// 创建新的更新触发器
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER);
// 创建新的删除触发器
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
}
// 获取NotesDatabaseHelper实例的静态同步方法
static synchronized NotesDatabaseHelper getInstance(Context context) {
// 如果实例尚未创建,则创建一个新的实例
if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context);
}
return mInstance; // 返回现有或新创建的实例
}
// 数据库创建时调用的方法
@Override
public void onCreate(SQLiteDatabase db) {
createNoteTable(db); //创建笔记表
createDataTable(db); // 创建数据表
}
// 数据库升级时调用的方法
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false; // 标识是否需要重新创建触发器
boolean skipV2 = false; // 标识是否跳过v2的升级
// 从版本1升级到版本2
if (oldVersion == 1) {
upgradeToV2(db); // 执行升级到v2的方法
// 设置跳过v2的标志因为此升级包含了从v2到v3的升级
skipV2 = true; // this upgrade including the upgrade from v2 to v3
oldVersion++; // 版本号自增
}
// 从版本2升级到版本3如果没有跳过v2
if (oldVersion == 2 && !skipV2) {
upgradeToV3(db); // 执行升级到v3的方法
reCreateTriggers = true; // 设置需要重新创建触发器的标志
oldVersion++; // 版本号自增
}
// 从版本3升级到版本4
if (oldVersion == 3) {
upgradeToV4(db); // 执行升级到v4的方法
oldVersion++; // 版本号自增
}
// 如果需要重新创建触发器,则执行相关方法
if (reCreateTriggers) {
reCreateNoteTableTriggers(db); // 重新创建笔记表的触发器
reCreateDataTableTriggers(db); // 重新创建数据表的触发器
}
// 检查最终的版本号是否与目标版本一致
if (oldVersion != newVersion) {
throw new IllegalStateException("Upgrade notes database to version " + newVersion
+ "fails"); // 抛出异常,表示升级失败
}
}
// 升级到版本2的方法
private void upgradeToV2(SQLiteDatabase db) {
// 如果存在,则删除笔记表
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
// 如果存在,则删除数据表
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
// 创建新的笔记表
createNoteTable(db);
// 创建新的数据表
createDataTable(db);
}
// 升级到版本3的方法
private void upgradeToV3(SQLiteDatabase db) {
// drop unused triggers 删除不再使用的触发器
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update");
// add a column for gtask id 在笔记表中添加用于谷歌任务的id列
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
+ " TEXT NOT NULL DEFAULT ''");
// add a trash system folder 添加一个垃圾箱系统文件
ContentValues values = new ContentValues(); // 创建ContentValues对象
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); // 设置垃圾箱文件夹的ID
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); // 设置类型为系统文件夹
db.insert(TABLE.NOTE, null, values); // 将垃圾箱文件夹插入到笔记表中
}
// 升级到版本4的方法
private void upgradeToV4(SQLiteDatabase db) {
// 在笔记表中添加版本列
//这行代码执行一个 SQL 命令,使用 ALTER TABLE 语句来修改 TABLE.NOTE 表
//ADD COLUMN 指令用于添加新列
//NoteColumns.VERSION 是新列的名称应该是一个整型INTEGER
//NOT NULL 约束确保该列不能为空
//DEFAULT 0 表示新列的默认值为0这意味着如果未提供值则该列会自动设置为0
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");
}
}