文章目录
  1. 前言
  2. Connection类
  3. Indexer类

前言

Connection类与Indexer类负责项目的数据库模块。Connection类相当于数据库的服务端;Indexer类相当于数据库的客户端。

Connection类

该类作为数据库服务端,提供管理数据库底层的接口;定义于src/musikcore/db/Connection.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Connection {
public:
DELETE_COPY_AND_ASSIGNMENT_DEFAULTS(Connection)

Connection() noexcept;
~Connection();

int Open(const std::string &database, unsigned int options = 0, unsigned int cache = 0);
/* 关闭数据库连接 */
int Close() noexcept;
/* 编译并执行SQL语句 */
int Execute(const char* sql);
/* 返回数据库连接D上最近一次成功插入到rowid表或虚拟表中的rowid */
int64_t LastInsertedId() noexcept;
/* 这些函数返回由唯一参数指定的数据库连接上最近完成的INSERT、UPDATE或DELETE语句修改、插入或删除的行数 */
int LastModifiedRowCount() noexcept;
/* 中断长时间运行的查询 */
void Interrupt();
/* 检查数据库:简而言之,sqlite3_wal_checkpoint(D,X)会将数据库连接D上的数据库X的预写日志中的内容传输到数据库文件中,并重置预写日志 */
void Checkpoint() noexcept;

private:
/* 初始化 */
void Initialize(unsigned int cache);
/* 更新引用计数 */
void UpdateReferenceCount(bool init);
/* 运行一条SQL语句 */
int StepStatement(sqlite3_stmt *stmt) noexcept;

friend class Statement;
friend class ScopedTransaction;
/* 该SQL连接上的事务数 */
int transactionCounter;
sqlite3 *connection;
/* 对象的互斥锁 */
std::mutex mutex;
};

Connection类有两个友元类Statement类(src/musikcore/db/Statement.h)与ScopedTransaction类(src/musikcore/db/ScopedTransaction.h)。ScopedTransaction类是对即时事务的封装,Statement类对应SQL语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class ScopedTransaction {
public:
DELETE_CLASS_DEFAULTS(ScopedTransaction)
/* 设置数据库连接并开启即时事务 */
ScopedTransaction(Connection &connection);
/* 提交事务并退出 */
~ScopedTransaction();
/* 取消事务的提交 */
void Cancel() noexcept;
/* 提交事务并开启新的即时事务 */
void CommitAndRestart();

private:
/* 开启即时事务 */
inline void Begin();
/* 提交事务(若事务取消则回滚,否则提交) */
inline void End();
/* 数据库连接 */
Connection *connection;
/* 标志事务是否取消 */
bool canceled;
};

class Statement {
public:
DELETE_CLASS_DEFAULTS(Statement)

Statement(const char* sql, Connection &connection) noexcept;
virtual ~Statement() noexcept;
/* 设置SQL语句的参数(如果有的话) */
void BindInt32(int position, int bindInt) noexcept;
void BindInt64(int position, int64_t bindInt) noexcept;
void BindFloat(int position, float bindFloat) noexcept;
void BindText(int position, const std::string& bindText);
void BindNull(int position) noexcept;
/* 获取SQL语句结果行第column列的值 */
const int ColumnInt32(int column) noexcept;
const int64_t ColumnInt64(int column) noexcept;
const float ColumnFloat(int column) noexcept;
const char* ColumnText(int column) noexcept;
/* 判断SQL语句结果行第column的列值是否为空 */
const bool IsNull(int column) noexcept;
/* 处理一行SQL语句并更新处理的行数 */
int Step();
/* 调用sqlite3_reset()函数将准备好的语句对象重置回初始状态,以便重新执行 */
void Reset() noexcept;
/* 重置准备好的语句上的所有参数绑定 */
void Unbind() noexcept;
void ResetAndUnbind() noexcept;

private:
friend class Connection;
/* 将SQL语句编译为字节码程序 */
Statement(Connection &connection) noexcept;
/* 编译的字节码程序 */
sqlite3_stmt *stmt;
/* 数据库连接 */
Connection *connection;
int modifiedRows;
};

Indexer类

该类作为数据库客户端,提供各种接口用于向数据库服务端发起相应的请求;定义于src/musikcore/db/Indexer.h,继承了类IIndexer、IIndexerWriter与IIndexerNotifier,IIndexerWriter类与IIndexerNotifier类仅声明了一些虚函数,Indexer类将它们实现。因此只额外解析IIndexer类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class Indexer :
public mymusic::core::IIndexer,
public mymusic::core::sdk::IIndexerWriter,
public mymusic::core::sdk::IIndexerNotifier
{
public:
/* 类构造函数(根据属性设置判断是否打开日志文件,设置tagReaders、audioDecoders与sources插件;设置dbFilename与libraryPath后连接数据库;查询paths所有的路径并按照id排序,再将结果存入paths中) */
Indexer(
const std::string& libraryPath,
const std::string& dbFilename);

Indexer(const Indexer&) = delete;
/* 若打开了日志文件logFile,则将其关闭;然后线程thread停止运行 */
virtual ~Indexer();

/* IIndexer */
/* 规范化路径并存入AddRemoveContext对象的path成员,add成员置true代表加入路径,路径存入paths,将要加入的AddRemoveContext对象存入addRemoveQueue */
void AddPath(const std::string& path) override;
/* 规范化路径并存入AddRemoveContext对象的path成员,add成员置false代表删除路径,路径从paths中删除,将要加入的AddRemoveContext对象存入addRemoveQueue */
void RemovePath(const std::string& path) override;
/* 将paths中存储所有的路径存入path字符串 */
void GetPaths(std::vector<std::string>& paths) override;
void Schedule(SyncType type) override;
/* 停止线程thread(清空syncQueue,状态切换为StateStopping,通知等待在stateMutex互斥锁对应的条件变量上的线程;调用join方法等待被创建线程的结束,并回收它的资源) */
void Stop() override;

State GetState() noexcept override {
return this->state;
}

/* IIndexerWriter */
/* 生成一个ITagStore对象 */
mymusic::core::sdk::ITagStore* CreateWriter() override;
bool RemoveByUri(mymusic::core::sdk::IIndexerSource* source, const char* uri) override;
bool RemoveByExternalId(mymusic::core::sdk::IIndexerSource* source, const char* id) override;
/* 删除source对应的所有轨迹 */
int RemoveAll(mymusic::core::sdk::IIndexerSource* source) override;
/* 事务提交程序 */
void CommitProgress(mymusic::core::sdk::IIndexerSource* source, unsigned updatedTracks) override;
/* 获取上一次修改的时间 */
int GetLastModifiedTime(mymusic::core::sdk::IIndexerSource* source, const char* id) override;

bool Save(
mymusic::core::sdk::IIndexerSource* source,
mymusic::core::sdk::ITagStore* store,
const char* externalId = "") override;

/* IIndexerNotifier */
/* 基于给定的新source重新Schedule */
void ScheduleRescan(mymusic::core::sdk::IIndexerSource* source) override;

private:
/* 要添加或删除的内容 */
struct AddRemoveContext {
bool add{ false };
std::string path;
};
/* 要同步的内容 */
struct SyncContext {
SyncType type;
int sourceId;
};
typedef std::vector<std::shared_ptr<
mymusic::core::sdk::ITagReader>> TagReaderList;
typedef std::vector<std::shared_ptr<
mymusic::core::sdk::IDecoderFactory>> DecoderList;
typedef std::vector<std::shared_ptr<
mymusic::core::sdk::IIndexerSource>> IndexerSourceList;
/* 主线程要运行的函数 */
void ThreadLoop();
/* 同步(基于数据库连接创建索引,处理AddRemoveQueue,删除不再具有相应源的轨迹,若context.type为SyncType::Rebuild则重建:移除所有分配了id的source对应的轨迹,type重置为SyncType::All;刷新sources;同步本地音乐资料到数据库并重新创建索引) */
void Synchronize(const SyncContext& context, boost::asio::io_service* io);
/* 同步的善后工作 */
void FinalizeSync(const SyncContext& context);
/* 删除数据库中所有不再引用有效路径条目的轨道 */
void SyncDelete();
/* 清理工作(将已经删除的音乐文件在数据库中相关的数据删除) */
void SyncCleanup();
/* 更新播放列表中轨道文件的次序 */
void SyncPlaylistTracksOrder();
/* 将source与paths指定路径下的内容同步 */
mymusic::core::sdk::ScanResult SyncSource(
mymusic::core::sdk::IIndexerSource* source,
const std::vector<std::string>& paths);
/* 处理AddRemoveQueue上要添加或删除的路径 */
void ProcessAddRemoveQueue();
/* 对数据库中genres、artists、albums、meta_values四个表排序 */
void SyncOptimize();
/* 对数据库中的每条轨道,调用每种解码器进行解析,只要有一个解码器可以解析,就将这条轨道解析后的数据保存下来 */
void RunAnalyzers();
/* 获取不再具有相应源的轨迹 */
std::set<int> GetOrphanedSourceIds();
/* 删除sourceId对应的所有轨迹 */
int RemoveAllForSourceId(int sourceId);
/* 调度:对stateMutex加锁,若thread为空,则生成thread对象;若成员为type与source->SourceId()的context在syncQueue中已存在,则退出;否则,将构造的该context对象加入syncQueue中;最后通知所有等待在stateMutex上的线程 */
void Schedule(SyncType type, mymusic::core::sdk::IIndexerSource *source);
/* 被扫描轨迹的数量增delta;若累计增加的扫描轨道数超过TRANSACTION_INTERVAL,则提交事务并开启新的即时事务,通知process的订阅者,incrementalUrisScanned置0 */
void IncrementTracksScanned(int delta = 1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
        /* 递归地遍历目录下的所有文件,若文件的扩展名对应的格式可解析,则调用ReadMetadataFromFile函数以流的形式从中读取数据 */
void SyncDirectory(
boost::asio::io_service* io,
const std::string& syncRoot,
const std::string& currentPath,
int64_t pathId);
/* 以流的形式从中读取数据 */
void ReadMetadataFromFile(
boost::asio::io_service* io,
const boost::filesystem::path& path,
const std::string& pathId);
/* 判断线程是否已退出或将要退出 */
bool Bail() noexcept;
/* 数据库连接 */
db::Connection dbConnection;
/* 资料库路径 */
std::string libraryPath;
/* 资料库名 */
std::string dbFilename;
/* 标志状态 */
std::atomic<State> state;
/* 状态的互斥锁 */
boost::mutex stateMutex;
/* 状态的条件变量 */
boost::condition waitCondition;
/* 线程对象 */
std::unique_ptr<std::thread> thread;
/* 数据库自上次提交事务以来,程序扫描轨道的次数;从程序开始运行起,程序扫描轨道的次数 */
std::atomic<int> incrementalUrisScanned, totalUrisScanned;
/* 要添加或删除路径组成的队列 */
std::deque<AddRemoveContext> addRemoveQueue;
/* 要同步的内容组成的队列 */
std::deque<SyncContext> syncQueue;
/* TagReader列表 */
TagReaderList tagReaders;
/* 解码器列表 */
DecoderList audioDecoders;
/* IndexerSource列表 */
IndexerSourceList sources;
/* 设置的属性 */
std::shared_ptr<mymusic::core::Preferences> prefs;
std::shared_ptr<mymusic::core::db::ScopedTransaction> trackTransaction;
/* 资料库所在目录 */
std::vector<std::string> paths;
/* 当前Source */
std::shared_ptr<mymusic::core::sdk::IIndexerSource> currentSource;
};

class IIndexer {
public:
/* 使用sigslot库传递消息,减少对象之间的耦合性 */
sigslot::signal0<> Started;
sigslot::signal1<int> Finished;
sigslot::signal1<int> Progress;
/* 枚举类型State来表示运行状态 */
enum State {
StateIdle = 0,
StateIndexing = 1,
StateStopping = 2,
StateStopped = 3
};
/* 枚举类型SyncType来表示同步类型 */
enum class SyncType {
All = 0,
Local = 1,
Rebuild = 2,
Sources = 3
};

virtual ~IIndexer() { }
virtual void AddPath(const std::string& path) = 0;
virtual void RemovePath(const std::string& path) = 0;
virtual void GetPaths(std::vector<std::string>& paths) = 0;
virtual void Schedule(SyncType type) = 0;
virtual void Stop() = 0;
virtual State GetState() = 0;
};

其中,Track类提供对轨道数据的各种读操作接口,TagStore类提供轨道数据的各种写操作接口;