数据库连接

Testing Is Documentation

tests/Database/DatabaseTest.php数据库连接 - 图1

Uses

  1. <?php
  2. use Exception;
  3. use Generator;
  4. use Leevel\Database\Database;
  5. use Leevel\Database\IDatabase;
  6. use Leevel\Database\Mysql;
  7. use Leevel\Database\Select;
  8. use Leevel\Filesystem\Helper;
  9. use PDO;
  10. use PDOException;
  11. use Tests\Database\DatabaseTestCase as TestCase;
  12. use Tests\MysqlNeedReconnectMock;
  13. use Throwable;

基本使用

  1. public function testBaseUse(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $database = $connect;
  5. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  6. $this->assertSame(
  7. 1,
  8. $database
  9. ->table('guest_book')
  10. ->insert($data),
  11. );
  12. $result = $database
  13. ->table('guest_book', 'name,content')
  14. ->where('id', 1)
  15. ->findOne();
  16. $this->assertSame('tom', $result->name);
  17. $this->assertSame('I love movie.', $result->content);
  18. }

query 查询数据记录

  1. public function testQuery(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('guest_book')
  9. ->insert($data),
  10. );
  11. $insertData = $connect->query('select * from guest_book where id=?', [1]);
  12. $insertData = (array) $insertData[0];
  13. $this->assertSame(1, $insertData['id']);
  14. $this->assertSame('tom', $insertData['name']);
  15. $this->assertSame('I love movie.', $insertData['content']);
  16. $this->assertStringContainsString(date('Y-m'), $insertData['create_at']);
  17. }

query 查询数据记录支持缓存

query 是一个底层查询方法支持直接设置缓存,实际上其它的查询都会走这个 query 查询方法。

query 原型

  1. # Leevel\Database\Database::query
  2. /**
  3. * 查询数据记录.
  4. *
  5. * @param bool|int $master
  6. *
  7. * @return mixed
  8. */
  9. public function query(string $sql, array $bindParams = [], $master = false, ?string $cacheName = null, ?int $cacheExpire = null, ?string $cacheConnect = null);
  1. public function testQueryCache(): void
  2. {
  3. $manager = $this->createDatabaseManager();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 5; $n++) {
  6. $manager
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $cacheDir = dirname(__DIR__).'/databaseCacheManager';
  11. $cacheFile = $cacheDir.'/testcachekey.php';
  12. $result = $manager
  13. ->table('guest_book')
  14. ->query('SELECT * FROM guest_book');
  15. $this->assertFileNotExists($cacheFile);
  16. $this->assertCount(6, $result);
  17. $this->assertSame(1, $result[0]->id);
  18. $this->assertSame('tom', $result[0]->name);
  19. $this->assertSame('I love movie.', $result[0]->content);
  20. $resultWithoutCache = $manager
  21. ->query('SELECT * FROM guest_book', [], false, 'testcachekey');
  22. // cached data
  23. $resultWithCache = $manager
  24. ->query('SELECT * FROM guest_book', [], false, 'testcachekey');
  25. $this->assertFileExists($cacheFile);
  26. $this->assertCount(6, $resultWithCache);
  27. $this->assertSame(1, $resultWithCache[0]->id);
  28. $this->assertSame('tom', $resultWithCache[0]->name);
  29. $this->assertSame('I love movie.', $resultWithCache[0]->content);
  30. $this->assertEquals($result, $resultWithCache);
  31. $this->assertFalse($result === $resultWithCache);
  32. $this->assertEquals($resultWithCache, $resultWithoutCache);
  33. }

execute 执行 SQL 语句

  1. public function testExecute(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $this->assertSame(1, $connect->execute('insert into guest_book (name, content) values (?, ?)', ['小鸭子', '喜欢游泳']));
  5. $insertData = $connect->query('select * from guest_book where id=?', [1]);
  6. $insertData = (array) $insertData[0];
  7. $this->assertSame(1, $insertData['id']);
  8. $this->assertSame('小鸭子', $insertData['name']);
  9. $this->assertSame('喜欢游泳', $insertData['content']);
  10. $this->assertStringContainsString(date('Y-m'), $insertData['create_at']);
  11. }

cursor 游标查询

cursor 游标查询可以节省内存。

cursor 原型

  1. # Leevel\Database\Database::cursor
  2. /**
  3. * 游标查询.
  4. *
  5. * @param bool|int $master
  6. */
  7. public function cursor(string $sql, array $bindParams = [], $master = false): Generator;
  1. public function testCursor(): void
  2. {
  3. $manager = $this->createDatabaseManager();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 5; $n++) {
  6. $manager
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $result = $manager->cursor('SELECT * FROM guest_book');
  11. $this->assertInstanceof(Generator::class, $result);
  12. $n = 1;
  13. foreach ($result as $v) {
  14. $this->assertSame($n, $v->id);
  15. $this->assertSame('tom', $v->name);
  16. $this->assertSame('I love movie.', $v->content);
  17. $n++;
  18. }
  19. }

select 原生 SQL 查询数据

  1. public function testSelect(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('guest_book')
  9. ->insert($data),
  10. );
  11. $insertData = $connect->select('select * from guest_book where id = ?', [1]);
  12. $insertData = (array) $insertData[0];
  13. $this->assertSame(1, $insertData['id']);
  14. $this->assertSame('tom', $insertData['name']);
  15. $this->assertSame('I love movie.', $insertData['content']);
  16. $this->assertStringContainsString(date('Y-m'), $insertData['create_at']);
  17. }

select 原生 SQL 查询数据支持参数绑定

  1. public function testSelectWithBind(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('guest_book')
  9. ->insert($data),
  10. );
  11. $insertData = $connect->select('select * from guest_book where id = :id', ['id' => 1]);
  12. $insertData = (array) $insertData[0];
  13. $this->assertSame(1, $insertData['id']);
  14. $this->assertSame('tom', $insertData['name']);
  15. $this->assertSame('I love movie.', $insertData['content']);
  16. $this->assertStringContainsString(date('Y-m'), $insertData['create_at']);
  17. }

insert 插入数据 insert (支持原生 SQL)

  1. public function testInsert(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $this->assertSame(1, $connect->insert('insert into guest_book (name, content) values (?, ?)', ['tom', 'I love movie.']));
  5. $insertData = $connect->select('select * from guest_book where id = :id', ['id' => 1]);
  6. $insertData = (array) $insertData[0];
  7. $this->assertSame(1, $insertData['id']);
  8. $this->assertSame('tom', $insertData['name']);
  9. $this->assertSame('I love movie.', $insertData['content']);
  10. $this->assertStringContainsString(date('Y-m'), $insertData['create_at']);
  11. }

update 更新数据 update (支持原生 SQL)

  1. public function testUpdate(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $this->assertSame(1, $connect->insert('insert into guest_book (name, content) values (?, ?)', ['tom', 'I love movie.']));
  5. $insertData = $connect->select('select * from guest_book where id = :id', ['id' => 1]);
  6. $insertData = (array) $insertData[0];
  7. $this->assertSame(1, $insertData['id']);
  8. $this->assertSame('tom', $insertData['name']);
  9. $this->assertSame('I love movie.', $insertData['content']);
  10. $this->assertStringContainsString(date('Y-m'), $insertData['create_at']);
  11. $this->assertSame(1, $connect->update('update guest_book set name = "小牛" where id = ?', [1]));
  12. $insertData = $connect->select('select * from guest_book where id = :id', ['id' => 1]);
  13. $insertData = (array) $insertData[0];
  14. $this->assertSame(1, $insertData['id']);
  15. $this->assertSame('小牛', $insertData['name']);
  16. $this->assertSame('I love movie.', $insertData['content']);
  17. $this->assertStringContainsString(date('Y-m'), $insertData['create_at']);
  18. }

delete 删除数据 delete (支持原生 SQL)

  1. public function testDelete(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $this->assertSame(1, $connect->insert('insert into guest_book (name, content) values (?, ?)', ['tom', 'I love movie.']));
  5. $insertData = $connect->select('select * from guest_book where id = :id', ['id' => 1]);
  6. $insertData = (array) $insertData[0];
  7. $this->assertSame(1, $insertData['id']);
  8. $this->assertSame('tom', $insertData['name']);
  9. $this->assertSame('I love movie.', $insertData['content']);
  10. $this->assertStringContainsString(date('Y-m'), $insertData['create_at']);
  11. $this->assertSame(1, $connect->delete('delete from guest_book where id = ?', [1]));
  12. $this->assertSame(0, $connect->table('guest_book')->findCount());
  13. }

transaction 执行数据库事务

  1. public function testTransaction(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 1; $n++) {
  6. $connect
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $this->assertSame(2, $connect->table('guest_book')->findCount());
  11. $connect->transaction(function ($connect) {
  12. $connect
  13. ->table('guest_book')
  14. ->where('id', 1)
  15. ->delete();
  16. $this->assertSame(1, $connect->table('guest_book')->findCount());
  17. $connect
  18. ->table('guest_book')
  19. ->where('id', 2)
  20. ->delete();
  21. $this->assertSame(0, $connect->table('guest_book')->findCount());
  22. });
  23. $this->assertSame(0, $connect->table('guest_book')->findCount());
  24. }

transaction 执行数据库事务回滚例子

  1. public function testTransactionRollback(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 1; $n++) {
  6. $connect
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $this->assertSame(2, $connect->table('guest_book')->findCount());
  11. $this->assertFalse($connect->inTransaction());
  12. try {
  13. $connect->transaction(function ($connect) {
  14. $connect->table('guest_book')->where('id', 1)->delete();
  15. $this->assertSame(1, $connect->table('guest_book')->findCount());
  16. $this->assertTrue($connect->inTransaction());
  17. throw new Exception('Will rollback');
  18. $connect->table('guest_book')->where('id', 2)->delete();
  19. });
  20. } catch (Throwable $e) {
  21. $this->assertSame('Will rollback', $e->getMessage());
  22. }
  23. $this->assertFalse($connect->inTransaction());
  24. $this->assertSame(2, $connect->table('guest_book')->findCount());
  25. }

beginTransaction.commit 启动事务和用于非自动提交状态下面的查询提交

  1. public function testTransactionByCustom(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 1; $n++) {
  6. $connect
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $this->assertSame(2, $connect->table('guest_book')->findCount());
  11. $connect->beginTransaction();
  12. $connect->table('guest_book')->where('id', 1)->delete();
  13. $this->assertSame(1, $connect->table('guest_book')->findCount());
  14. $connect->table('guest_book')->where('id', 2)->delete();
  15. $this->assertSame(0, $connect->table('guest_book')->findCount());
  16. $connect->commit();
  17. $this->assertSame(0, $connect->table('guest_book')->findCount());
  18. }

beginTransaction.rollBack 启动事务和事务回滚

  1. public function testTransactionRollbackByCustom(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 1; $n++) {
  6. $connect
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $this->assertSame(2, $connect->table('guest_book')->findCount());
  11. $this->assertFalse($connect->inTransaction());
  12. try {
  13. $connect->beginTransaction();
  14. $connect
  15. ->table('guest_book')
  16. ->where('id', 1)
  17. ->delete();
  18. $this->assertSame(1, $connect->table('guest_book')->findCount());
  19. $this->assertTrue($connect->inTransaction());
  20. throw new Exception('Will rollback');
  21. $connect->table('guest_book')->where('id', 2)->delete();
  22. $connect->commit();
  23. } catch (Throwable $e) {
  24. $this->assertSame('Will rollback', $e->getMessage());
  25. $connect->rollBack();
  26. }
  27. $this->assertFalse($connect->inTransaction());
  28. $this->assertSame(2, $connect->table('guest_book')->findCount());
  29. }

procedure 查询存储过程数据记录

  1. public function testCallProcedure(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 1; $n++) {
  6. $connect
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $result = $connect->procedure('CALL test_procedure(0)');
  11. $data = <<<'eot'
  12. [
  13. [
  14. {
  15. "name": "tom"
  16. },
  17. {
  18. "name": "tom"
  19. }
  20. ],
  21. [
  22. {
  23. "content": "I love movie."
  24. }
  25. ]
  26. ]
  27. eot;
  28. $this->assertSame(
  29. $data,
  30. $this->varJson(
  31. $result
  32. )
  33. );
  34. }

procedure 查询存储过程数据记录支持参数绑定

  1. public function testCallProcedure2(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 1; $n++) {
  6. $connect
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $result = $connect->procedure('CALL test_procedure2(0,:name)', [
  11. 'name' => [null, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 200],
  12. ]);
  13. $data = <<<'eot'
  14. [
  15. [
  16. {
  17. "content": "I love movie."
  18. }
  19. ],
  20. [
  21. {
  22. "_name": "tom"
  23. }
  24. ]
  25. ]
  26. eot;
  27. $this->assertSame(
  28. $data,
  29. $this->varJson(
  30. $result
  31. )
  32. );
  33. }

查询存储过程数据支持原生方法

  1. public function testCallProcedure3(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 1; $n++) {
  6. $connect
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $pdoStatement = $connect->pdo(true)->prepare('CALL test_procedure2(0,:name)');
  11. $outName = null;
  12. $pdoStatement->bindParam(':name', $outName, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 200);
  13. $pdoStatement->execute();
  14. $result = [];
  15. do {
  16. try {
  17. $result[] = $pdoStatement->fetchAll(PDO::FETCH_OBJ);
  18. } catch (PDOException $e) {
  19. }
  20. } while ($pdoStatement->nextRowset());
  21. $data = <<<'eot'
  22. [
  23. [
  24. {
  25. "content": "I love movie."
  26. }
  27. ],
  28. [
  29. {
  30. "_name": "tom"
  31. }
  32. ]
  33. ]
  34. eot;
  35. $this->assertSame(
  36. $data,
  37. $this->varJson(
  38. $result
  39. )
  40. );
  41. }

查询存储过程数据支持缓存

procedure 是一个底层查询方法支持直接设置缓存。

procedure 原型

  1. # Leevel\Database\Database::procedure
  2. /**
  3. * 查询存储过程数据记录.
  4. *
  5. * @param bool|int $master
  6. */
  7. public function procedure(string $sql, array $bindParams = [], $master = false, ?string $cacheName = null, ?int $cacheExpire = null, ?string $cacheConnect = null): array;
  1. public function testCacheProcedure(): void
  2. {
  3. $manager = $this->createDatabaseManager();
  4. $data = ['name' => 'tom', 'content' => 'I love movie.'];
  5. for ($n = 0; $n <= 1; $n++) {
  6. $manager
  7. ->table('guest_book')
  8. ->insert($data);
  9. }
  10. $cacheDir = dirname(__DIR__).'/databaseCacheManager';
  11. $cacheFile = $cacheDir.'/testcachekey.php';
  12. $result = $manager
  13. ->procedure('CALL test_procedure(0)');
  14. $this->assertFileNotExists($cacheFile);
  15. $data = <<<'eot'
  16. [
  17. [
  18. {
  19. "name": "tom"
  20. },
  21. {
  22. "name": "tom"
  23. }
  24. ],
  25. [
  26. {
  27. "content": "I love movie."
  28. }
  29. ]
  30. ]
  31. eot;
  32. $this->assertSame(
  33. $data,
  34. $this->varJson(
  35. $result
  36. )
  37. );
  38. $resultWithoutCache = $manager
  39. ->procedure('CALL test_procedure(0)', [], false, 'testcachekey');
  40. $this->assertFileExists($cacheFile);
  41. // cached data
  42. $resultWithCache = $manager
  43. ->procedure('CALL test_procedure(0)', [], false, 'testcachekey');
  44. $this->assertFileExists($cacheFile);
  45. $this->assertSame(
  46. $data,
  47. $this->varJson(
  48. $resultWithCache
  49. )
  50. );
  51. $this->assertEquals($result, $resultWithCache);
  52. $this->assertFalse($result === $resultWithCache);
  53. $this->assertEquals($resultWithCache, $resultWithoutCache);
  54. }

pdo 返回 PDO 查询连接

  1. public function testPdo(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $this->assertNull($connect->pdo(IDatabase::MASTER));
  5. $this->assertInstanceof(PDO::class, $connect->pdo(true));
  6. $this->assertInstanceof(PDO::class, $connect->pdo(IDatabase::MASTER));
  7. $this->assertNull($connect->pdo(5));
  8. $connect->close();
  9. }

setSavepoints 设置是否启用部分事务回滚保存点

  1. public function testBeginTransactionWithCreateSavepoint(): void
  2. {
  3. if (isset($_SERVER['TRAVIS_COMMIT'])) {
  4. $this->markTestSkipped('Mysql of travis-ci not support savepoint.');
  5. return;
  6. }
  7. $connect = $this->createDatabaseConnect();
  8. $connect->setSavepoints(true);
  9. $connect->beginTransaction();
  10. $connect
  11. ->table('guest_book')
  12. ->insert(['name' => 'tom']); // `tom` will not rollBack
  13. $connect->beginTransaction();
  14. $this->assertSame('SAVEPOINT trans2', $connect->getLastSql());
  15. $connect
  16. ->table('guest_book')
  17. ->insert(['name' => 'jerry']);
  18. $connect->rollBack();
  19. $this->assertSame('ROLLBACK TO SAVEPOINT trans2', $connect->getLastSql());
  20. $connect->commit();
  21. $book = $connect
  22. ->table('guest_book')
  23. ->where('id', 1)
  24. ->findOne();
  25. $this->assertSame(1, $connect->table('guest_book')->findCount());
  26. $this->assertSame('tom', $book->name);
  27. }

setSavepoints 设置是否启用部分事务提交保存点

  1. public function testCommitWithReleaseSavepoint(): void
  2. {
  3. if (isset($_SERVER['TRAVIS_COMMIT'])) {
  4. $this->markTestSkipped('Mysql of travis-ci not support savepoint.');
  5. return;
  6. }
  7. $connect = $this->createDatabaseConnect();
  8. $connect->setSavepoints(true);
  9. $connect->beginTransaction();
  10. $connect
  11. ->table('guest_book')
  12. ->insert(['name' => 'tom']);
  13. $connect->beginTransaction();
  14. $this->assertSame('SAVEPOINT trans2', $connect->getLastSql());
  15. $connect
  16. ->table('guest_book')
  17. ->insert(['name' => 'jerry']);
  18. $connect->commit();
  19. $this->assertSame('RELEASE SAVEPOINT trans2', $connect->getLastSql());
  20. $connect->commit();
  21. $book = $connect
  22. ->table('guest_book')
  23. ->where('id', 1)
  24. ->findOne();
  25. $book2 = $connect
  26. ->table('guest_book')
  27. ->where('id', 2)
  28. ->findOne();
  29. $this->assertSame(2, $connect->table('guest_book')->findCount());
  30. $this->assertSame('tom', $book->name);
  31. $this->assertSame('jerry', $book2->name);
  32. }

numRows 返回影响记录

  1. public function testNumRows(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $this->assertSame(0, $connect->numRows());
  5. $connect
  6. ->table('guest_book')
  7. ->insert(['name' => 'jerry', 'content' => '']);
  8. $this->assertSame(1, $connect->numRows());
  9. $connect
  10. ->table('guest_book')
  11. ->where('id', 1)
  12. ->update(['name' => 'jerry']);
  13. $this->assertSame(0, $connect->numRows());
  14. $connect
  15. ->table('guest_book')
  16. ->where('id', 1)
  17. ->update(['name' => 'tom']);
  18. $this->assertSame(1, $connect->numRows());
  19. }

数据库主从

数据库配置项 distributed 表示主从,如果从数据库均连接失败,则还是会走主库。

  1. public function testReadConnectDistributed(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock([
  4. 'driver' => 'mysql',
  5. 'separate' => false,
  6. 'distributed' => true,
  7. 'master' => [
  8. 'host' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['HOST'],
  9. 'port' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PORT'],
  10. 'name' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['NAME'],
  11. 'user' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['USER'],
  12. 'password' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PASSWORD'],
  13. 'charset' => 'utf8',
  14. 'options' => [
  15. PDO::ATTR_PERSISTENT => false,
  16. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  17. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  18. PDO::ATTR_STRINGIFY_FETCHES => false,
  19. PDO::ATTR_EMULATE_PREPARES => false,
  20. PDO::ATTR_TIMEOUT => 30,
  21. ],
  22. ],
  23. 'slave' => [
  24. [
  25. 'host' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['HOST'],
  26. 'port' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PORT'],
  27. 'name' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['NAME'],
  28. 'user' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['USER'],
  29. 'password' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PASSWORD'],
  30. 'charset' => 'utf8',
  31. 'options' => [
  32. PDO::ATTR_PERSISTENT => false,
  33. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  34. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  35. PDO::ATTR_STRINGIFY_FETCHES => false,
  36. PDO::ATTR_EMULATE_PREPARES => false,
  37. PDO::ATTR_TIMEOUT => 30,
  38. ],
  39. ],
  40. [
  41. 'host' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['HOST'],
  42. 'port' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PORT'],
  43. 'name' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['NAME'],
  44. 'user' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['USER'],
  45. 'password' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PASSWORD'],
  46. 'charset' => 'utf8',
  47. 'options' => [
  48. PDO::ATTR_PERSISTENT => false,
  49. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  50. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  51. PDO::ATTR_STRINGIFY_FETCHES => false,
  52. PDO::ATTR_EMULATE_PREPARES => false,
  53. PDO::ATTR_TIMEOUT => 30,
  54. ],
  55. ],
  56. ],
  57. ]);
  58. $this->assertInstanceof(PDO::class, $connect->pdo());
  59. $connect->close();
  60. }

数据库读写分离

数据库配置项 separate 表示读写分离,如果从数据库均连接失败,则读数据还是会走主库。

  1. public function testReadConnectDistributedButAllInvalidAndAlsoIsSeparate(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock([
  4. 'driver' => 'mysql',
  5. 'separate' => true,
  6. 'distributed' => true,
  7. 'master' => [
  8. 'host' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['HOST'],
  9. 'port' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PORT'],
  10. 'name' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['NAME'],
  11. 'user' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['USER'],
  12. 'password' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PASSWORD'],
  13. 'charset' => 'utf8',
  14. 'options' => [
  15. PDO::ATTR_PERSISTENT => false,
  16. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  17. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  18. PDO::ATTR_STRINGIFY_FETCHES => false,
  19. PDO::ATTR_EMULATE_PREPARES => false,
  20. PDO::ATTR_TIMEOUT => 30,
  21. ],
  22. ],
  23. 'slave' => [
  24. [
  25. 'host' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['HOST'],
  26. 'port' => '5555', // not invalid
  27. 'name' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['NAME'],
  28. 'user' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['USER'],
  29. 'password' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PASSWORD'],
  30. 'charset' => 'utf8',
  31. 'options' => [
  32. PDO::ATTR_PERSISTENT => false,
  33. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  34. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  35. PDO::ATTR_STRINGIFY_FETCHES => false,
  36. PDO::ATTR_EMULATE_PREPARES => false,
  37. PDO::ATTR_TIMEOUT => 30,
  38. ],
  39. ],
  40. [
  41. 'host' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['HOST'],
  42. 'port' => '6666', // not invalid
  43. 'name' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['NAME'],
  44. 'user' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['USER'],
  45. 'password' => $GLOBALS['LEEVEL_ENV']['DATABASE']['MYSQL']['PASSWORD'],
  46. 'charset' => 'utf8',
  47. 'options' => [
  48. PDO::ATTR_PERSISTENT => false,
  49. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  50. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  51. PDO::ATTR_STRINGIFY_FETCHES => false,
  52. PDO::ATTR_EMULATE_PREPARES => false,
  53. PDO::ATTR_TIMEOUT => 30,
  54. ],
  55. ],
  56. ],
  57. ]);
  58. $this->assertInstanceof(PDO::class, $connect->pdo());
  59. $this->assertInstanceof(PDO::class, $connect->pdo());
  60. $connect->close();
  61. }

databaseSelect 返回查询对象

  1. public function testDatabaseSelectIsNotInit(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $this->assertInstanceof(Select::class, $connect->databaseSelect());
  5. }

getTableNames 取得数据库表名列表

  1. public function testGetTableNames(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $result = $connect->getTableNames('test');
  5. $this->assertTrue(in_array('guest_book', $result, true));
  6. }

getTableColumns 取得数据库表字段信息

  1. public function testGetTableColumns(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $result = $connect->getTableColumns('guest_book');
  5. $sql = <<<'eot'
  6. {
  7. "list": {
  8. "id": {
  9. "field": "id",
  10. "type": "bigint(20)",
  11. "collation": null,
  12. "null": false,
  13. "key": "PRI",
  14. "default": null,
  15. "extra": "auto_increment",
  16. "comment": "ID",
  17. "primary_key": true,
  18. "type_name": "bigint",
  19. "type_length": "20",
  20. "auto_increment": true
  21. },
  22. "name": {
  23. "field": "name",
  24. "type": "varchar(64)",
  25. "collation": "utf8_general_ci",
  26. "null": false,
  27. "key": "",
  28. "default": "",
  29. "extra": "",
  30. "comment": "名字",
  31. "primary_key": false,
  32. "type_name": "varchar",
  33. "type_length": "64",
  34. "auto_increment": false
  35. },
  36. "content": {
  37. "field": "content",
  38. "type": "longtext",
  39. "collation": "utf8_general_ci",
  40. "null": false,
  41. "key": "",
  42. "default": null,
  43. "extra": "",
  44. "comment": "评论内容",
  45. "primary_key": false,
  46. "type_name": "longtext",
  47. "type_length": null,
  48. "auto_increment": false
  49. },
  50. "create_at": {
  51. "field": "create_at",
  52. "type": "datetime",
  53. "collation": null,
  54. "null": false,
  55. "key": "",
  56. "default": "CURRENT_TIMESTAMP",
  57. "extra": "",
  58. "comment": "创建时间",
  59. "primary_key": false,
  60. "type_name": "datetime",
  61. "type_length": null,
  62. "auto_increment": false
  63. }
  64. },
  65. "primary_key": [
  66. "id"
  67. ],
  68. "auto_increment": "id",
  69. "table_collation": "utf8_general_ci",
  70. "table_comment": "留言板"
  71. }
  72. eot;
  73. $this->assertSame(
  74. $sql,
  75. $this->varJson(
  76. $result
  77. )
  78. );
  79. }

getRawSql 游标查询

getRawSql 返回原生查询真实 SQL,以便于更加直观。

getRawSql 原型

  1. # Leevel\Database\Database::getRawSql
  2. /**
  3. * 从 PDO 预处理语句中获取原始 SQL 查询字符串.
  4. *
  5. * - This method borrows heavily from the pdo-debug package and is part of the pdo-debug package.
  6. *
  7. * @see https://github.com/panique/pdo-debug/blob/master/pdo-debug.php
  8. * @see https://stackoverflow.com/questions/210564/getting-raw-sql-query-string-from-pdo-prepared-statements
  9. * @see http://php.net/manual/en/pdo.constants.php
  10. */
  11. public static function getRawSql(string $sql, array $bindParams): string;
  1. public function testGetRawSql(): void
  2. {
  3. $sql = Database::getRawSql('SELECT * FROM guest_book WHERE id = :id', [
  4. ':id' => [1],
  5. ]);
  6. $this->assertSame($sql, 'SELECT * FROM guest_book WHERE id = 1');
  7. }