19. DB访问Part 1: MySQL

iFun引擎的ORM已实现了用于管理游戏对象的DB自动化处理,但根据情况的不同,也会有须要无视ORM而写入或读取DB数据等直接访问DB服务器的情况。 此时,可使用下面介绍的MySQL Connector,在与MySQL兼容的所有DB中进行SQL处理。

Important

该功能并不是为了与ORM并行使用,而是为了对那些因为须要访问运营工具或其他单独的table而导致ORM无法处理的内容进行处理。如果是为了处理游戏对象,请参考ORM。

如果通过MySQL Connector同时访问ORM所管理的对象,有可能会造成数据流失或失灵,十分危险。 相关内容请参考 从游戏服务器外部访问 DB 时注意事项

Note

虽然也可以通过直接导入MySQL client库的方式来使用,但使用iFun引擎提供的class,可以在处理时不用注意线程干扰问题,故会更加便利。

Tip

以下的说明内容并没有覆盖所有功能,具体内容请参考 MariaDB class

19.1. DB连接初始化

19.1.1. 指定参数值

Ptr<Mariadb> Mariadb::Create(host, user, password, schema,
                             num_connections [, charset] [, auto_retry_on_deadlock])
Mariadb Mariadb.Create(host, user, password, schema,
                       num_connections [, charset] [, auto_retry_on_deadlock])

指定用于连接DB的参数值。

host

输入DB服务器的地址。例) tcp://127.0.0.1:3306

user

输入DB账户ID。

password

输入DB账户Password。

schema

输入DB名。

num_connections

输入Connection Pool的连接数。

charset

输入Connection的Character Set。如省略,则使用DB默认值。

auto_retry_on_deadlock

敬请参考http://dev.mysql.com/doc/refman/5.5/en/innodb-deadlocks.html中的说明。如省略,则为false。

Note

若Character Set与已生成的DB/Table/Column不同,字符串 有可能会成乱码。

Note

当Connection数为2个以上时,将并行执行,因此不保证Query的顺序。

Note

num_connections 较大时,部分连接将会失败,或在一定时间后 输出连接断线的日志。请在MySQL的设置中将 max_connectionsbacklogmax_allowed_packet 值调高。

19.1.2. Thread pool初始化

void Mariadb::Initialize()
void Mariadb.Start()

按照通过 Create() 指定的连接数来初始化connection pool。从该函数被调用后开始,即可执行query。

19.2. 执行Query

使用以下函数中的一个。结尾带有 Sync 的函数是通过同步方式运行的函数。

void Mariadb::ExecuteQuery(query, callback)
Ptr<ResultSets> Mariadb::ExecuteQuerySync(query, Mariadb::Error *error)
public void Mariadb.ExecuteQuery(query, callback)
public ResultSets ExecuteQuerySync(string query, out Error error)

query

欲执行的SQL Query字符串。

callback

处理执行结果的Callback函数。

error

发生错误时,传输错误值的output变量。

Note

callback须为 void(const Ptr<ResultSets> &result_sets, const Mariadb::Error &error) 形式。

Note

当前版本不支持Prepared Statement。

19.3. MySQL connector使用示例

下面的示例假设了如下所示的Table和Procedure。

Member table:

===================
No.  Name.  Weight.
-------------------
0    dog    12.5
1    cat    2.1
2    bird   0.5
3    tiger  290.0

ExampleTable table:

CREATE TABLE ExampleTable (date DATETIME)

ExampleProc procedure:

CREATE PROCEDURE ExampleProc ()
BEGIN
  SELECT * FROM Member;
END

ExampleProc2 procedure:

CREATE PROCEDURE ExampleProc2 (OUT param1 INT, OUT param2 CHAR(50))
BEGIN
  SET param1 = 1234;
  SET param2 = "test";
END

19.3.1. 示例-SELECT

下面的示例输出了如下所示的结果。

====================
No: 0, Name: dog, Weight: 12.5
No: 3, Name: tiger, Weight: 290
====================
No: 2, Name: bird, Weight: 0.5
 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
Ptr<Mariadb> connections;

void Initialize() {
  connections = Mariadb::Create("tcp://127.0.0.1:3306", "myid", "mypw",
                                "mydb", 5, "utf8", false);
  connections->Initialize();
}

void ExecuteQuery() {
  std::stringstream query;
  query << "SELECT * FROM Member WHERE Weight > 10.0;"
        << "SELECT * FROM Member WHERE Weight < 1.0;";

  connections->ExecuteQuery(query.str(), OnQueryExecuted);
}

void OnQueryExecuted(const Ptr<ResultSets> &result_sets,
                     const Mariadb::Error &error) {
  if (error.code != 0) {
    LOG(ERROR) << "Error: code=" << error.code << ", desc=" << error.desc;
    return;
  }

  // As many as SELECT queries.
  while (result_sets->SeekNextResultSet()) {
    LOG(INFO) << "=====================";

    // As many as result rows of each query.
    while (result_sets->SeekNextRow()) {
      LOG(INFO) << "No: " << result_sets->GetInt(0)
                << ", Name: " << result_sets->GetString(1)
                << ", Weight: " << result_sets->GetDouble(2);
    }
  }
}
 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
Mariadb connections;

void Initialize() {
  Mariadb connections = Mariadb.Create (
      "tcp://127.0.0.1:3306", "funapi", "funapi", "funapi", 8, "utf8");

  connections.Start();
}

void ExecuteQuery() {
  StringBuilder strbuilder = new StringBuilder ();
  strbuilder.Append ("SELECT * FROM Member WHERE Weight > 10.0;");
  strbuilder.Append ("SELECT * FROM Member WHERE Weight < 1.0;");

  connections.ExecuteQuery (query.ToString(), OnQueryExecuted);
}

public void OnQueryExecuted(ResultSets result_sets, Mariadb.Error error)
{
  if (error.Code != 0)
  {
    Log.Info ("Error: code={0}, desc={1}", error.Code, error.Desc);
  }

  // As many as SELECT queries.
  while (result_sets.SeekNextResultSet ()) {
    Log.Info ("=====================");

    // As many as result rows of each query.
    while (result_sets.SeekNextRow ()) {
      Log.Info ("No: {0}", result_sets.GetString (0));  // 또는 GetInt("No")
      Log.Info (", Name: {0}", result_sets.GetString (1));
      Log.Info (", Weight: {0}", result_sets.GetDouble (2));
    }
  }
}

19.3.2. 示例-INSERT

下面是INSERT后通过SELECT来输出结果的示例。输出如下结果。

2016-06-14 16:23:19
 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
Ptr<Mariadb> connections;

void Initialize() {
  connections = Mariadb::Create("tcp://127.0.0.1:3306", "myid", "mypw",
                                "mydb", 5, "utf8", false);
  connections->Initialize();
}

void ExecuteQuery() {
  string now = boost::posix_time::to_iso_extended_string(WallClock::Now());

  std::stringstream query;
  query << "INSERT INTO ExampleTable VALUES('" << now << "');";

  Mariadb::Error error;
  connections->ExecuteQuerySync(query.str(), &error);
  if (error.code != 0) {
    LOG(ERROR) << "Error: code=" << error.code << ", desc=" << error.desc;
    return;
  }

  string query2 = "SELECT date FROM ExampleTable;";
  connections->ExecuteQuery(query2, OnQueryExecuted);
}

void OnQueryExecuted(const Ptr<ResultSets> &result_sets,
                     const Mariadb::Error &error) {
  if (error.code != 0) {
    LOG(ERROR) << "Error: code=" << error.code << ", desc=" << error.desc;
    return;
  }
  if (not result_sets->SeekNextResultSet()) {
    LOG(ERROR) << "no result set.";
    return;
  }

  while (result_sets->SeekNextRow()) {
    WallClock::Value datetime = result_sets->GetDateTime(0);
    LOG(INFO) << "datetime: " << datetime;
  }
}
 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
Mariadb connections;

void Initialize() {
  Mariadb connections = Mariadb.Create (
      "tcp://127.0.0.1:3306", "funapi", "funapi", "funapi", 8, "utf8");

  connections.Start();
}

// 쿼리 실행
void ExecuteQuery()
{
  string query = "INSERT INTO ExampleTable VALUES('{0}');";
        string now = WallClock.GetTimestring(WallClock.Now);

  Mariadb.Error error;
  connections.ExecuteQuerySync (String.Format (query, now), out error);

  if (error.Code != 0)
  {
    Log.Info ("Error: code={0}, desc={1}", error.Code, error.Desc);
  }

  string query2 = "SELECT date FROM ExampleTable;";

  connections.ExecuteQuery(query2, OnQueryExecuted);
}

public void OnQueryExecuted(ResultSets result_sets, Mariadb.Error error)
{
  if (error.Code != 0)
  {
    Log.Info ("Error: code={0}, desc={1}", error.Code, error.Desc);
  }

  if (!result_sets.SeekNextResultSet ()) {
    Log.Error ("no result set.");
    return;
  }

  while (result_sets.SeekNextRow ()) {
    System.DateTime dateTime = result_sets.GetDateTime (0);
    Log.Info ("Datetime : {0}", dateTime);
  }
}

19.3.3. 示例-利用Procedure进行SELECT

下面的示例是调用了生成如下所示结果值的Procedure的示例,它代替了直接通过SELECT获取结果。

No: 0, Name: dog, Weight: 12.5
No: 1, Name: cat, Weight: 2.1
No: 2, Name: bird, Weight: 0.5
No: 3, Name: tiger, Weight: 290
 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
Ptr<Mariadb> connections;

void Initialize() {
  connections = Mariadb::Create("tcp://127.0.0.1:3306", "myid", "mypw",
                                "mydb", 5, "utf8", false);
  connections->Initialize();
}

void ExecuteQuery() {
  std::stringstream query;
  query << "CALL ExampleProc();";

  connections->ExecuteQuery(query.str(), OnQueryExecuted);
}

void OnQueryExecuted(const Ptr<ResultSets> &result_sets,
                     const Mariadb::Error &error) {
  if (error.code != 0) {
    LOG(ERROR) << "Error: code=" << error.code << ", desc=" << error.desc;
    return;
  }

  if (not result_sets->SeekNextResultSet()) {
    LOG(ERROR) << "no result set.";
    return;
  }

  while (result_sets->SeekNextRow()) {
    LOG(INFO) << "No: " << result_sets->GetInt(0)
              << ", Name: " << result_sets->GetString(1)
              << ", Weight: " << result_sets->GetDouble(2);
  }
}
 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
Mariadb connections;

void Initialize() {
  Mariadb connections = Mariadb.Create (
      "tcp://127.0.0.1:3306", "funapi", "funapi", "funapi", 8, "utf8");

  connections.Start();
}

public void ExecuteQuery(Mariadb connections)
{
  connections.ExecuteQuery ("CALL ExampleProc();", OnQueryExecuted);
}

public void OnQueryExecuted(ResultSets result_sets, Mariadb.Error error)
{
  if (error.Code != 0)
  {
    Log.Info ("Error: code={0}, desc={1}", error.Code, error.Desc);
  }

  while (result_sets.SeekNextResultSet ()) {
    while (result_sets.SeekNextRow ()) {
      Log.Info ("No: {0}", result_sets.GetInt (0));  // 또는 GetInt("No")
      Log.Info (", Name: {0}", result_sets.GetString (1));
      Log.Info (", Weight: {0}", result_sets.GetDoubl e(2));
    }
  }
}

19.3.4. 示例-获取Procedure的OUTPUT

下面是使用Procedure的OUT参数的示例。

Note

当前版本不支持直接接收OUT参数。 代入到SQL Session变量中,进行SELECT。

p1: 1234, p2: test
 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
Ptr<Mariadb> connections;

void Initialize() {
  connections = Mariadb::Create("tcp://127.0.0.1:3306", "myid", "mypw",
                                "mydb", 5, "utf8", false);
  connections->Initialize();
}

void ExecuteQuery() {
  std::stringstream query;
  query << "CALL ExampleProc2(@p1, @p2);"
        << "SELECT @p1, @p2;";

  connections->ExecuteQuery(query.str(), OnQueryExecuted);
}

void OnQueryExecuted(const Ptr<ResultSets> &result_sets,
                     const Mariadb::Error &error) {
  if (error.code != 0) {
    LOG(ERROR) << "Error: code=" << error.code << ", desc=" << error.desc;
    return;
  }

  if (not result_sets->SeekNextResultSet()) {
    LOG(ERROR) << "no result set.";
    return;
  }

  while (result_sets->SeekNextRow()) {
    LOG(INFO) << "p1: " << result_sets->GetInt(0)
              << ", p2: " << result_sets->GetString(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
Mariadb connections;

void Initialize() {
  Mariadb connections = Mariadb.Create (
      "tcp://127.0.0.1:3306", "funapi", "funapi", "funapi", 8, "utf8");

  connections.Start();
}

void ExecuteQuery()
{
  StringBuilder query = new StringBuilder ();
  query.Append ("CALL ExampleProc2(@p1, @p2);");
  query.Append ("SELECT @p1, @p2;");
  connections.ExecuteQuery (query.ToString(), OnQueryExecuted);
}

public void OnQueryExecuted(ResultSets result_sets, Mariadb.Error error)
{
  if (error.Code != 0)
  {
    Log.Info ("Error: code={0}, desc={1}", error.Code, error.Desc);
  }

  if (!result_sets.SeekNextResultSet ()) {
    Log.Error ("no result set.");
    return;
  }

  while (result_sets.SeekNextRow ()) {
    Log.Info ("P1 : {0}", result_sets.GetInt (0));
    Log.Info (", P2 : {0}", result_sets.GetString (1));
  }
}