<?php
/*
* Copyright (c) 2018, 北京博习园教育科技有限公司
* All rights reserved.
*
* 文件名称: xmodel.php
* 摘 要: 模型公共类
* 作 者: 刘泉皓
* $Id$
*/ /**
* 数据库连接类,依赖 PDO_MYSQL 扩展
* walkor:在 https://github.com/auraphp/Aura.SqlQuery 的基础上修改而成
* liuquanhao:在 https://github.com/walkor/mysql 的基础上修改而成
*/ require_once __DIR__ . '/' . '../../../config/config.php';
require_once __DIR__ . '/' . '../function/cache.php';
require_once __DIR__ . '/' . '../function/comm_func.php'; class XModel
{
/**
* SELECT
*
* @var array
*/
protected $union = array(); /**
* 是否是更新
*
* @var bool
*/
protected $for_update = false; /**
* 选择的列
*
* @var array
*/
protected $cols = array(); /**
* 从哪些表里面 SELECT
*
* @var array
*/
protected $from = array(); /**
* $from 当前的 key
*
* @var int
*/
protected $from_key = -1; /**
* GROUP BY 的列
*
* @var array
*/
protected $group_by = array(); /**
* HAVING 条件数组.
*
* @var array
*/
protected $having = array(); /**
* HAVING 语句中绑定的值.
*
* @var array
*/
protected $bind_having = array(); /**
* 每页多少条记录
*
* @var int
*/
protected $paging = 10; /**
* sql 中绑定的值
*
* @var array
*/
protected $bind_values = array(); /**
* WHERE 条件.
*
* @var array
*/
protected $where = array(); /**
* WHERE 语句绑定的值
*
* @var array
*/
protected $bind_where = array(); /**
* ORDER BY 的列
*
* @var array
*/
protected $order_by = array(); /**
* ORDER BY 的排序方式,默认为升序
*
* @var bool
*/
protected $order_asc = true;
/**
* SELECT 多少记录
*
* @var int
*/
protected $limit = 0; /**
* 返回记录的游标
*
* @var int
*/
protected $offset = 0; /**
* flags 列表
*
* @var array
*/
protected $flags = array(); /**
* 操作哪个表
*
* @var string
*/
protected $table; /**
* 表.列 和 last-insert-id 映射
*
* @var array
*/
protected $last_insert_id_names = array(); /**
* INSERT 或者 UPDATE 的列
*
* @param array
*/
protected $col_values; /**
* 返回的列
*
* @var array
*/
protected $returning = array(); /**
* sql 的类型 SELECT INSERT DELETE UPDATE
*
* @var string
*/
protected $type = ''; /**
* pdo 实例
*
* @var PDO
*/
protected $pdo; /**
* PDOStatement 实例
*
* @var PDOStatement
*/
protected $sQuery; /**
* 数据库用户名密码等配置
*
* @var array
*/
protected $readSettings = array();
protected $writeSettings = array(); /**
* sql 的参数
*
* @var array
*/
protected $parameters = array(); /**
* 最后一条直行的 sql
*
* @var string
*/
protected $lastSql = ''; /**
* 使用memcache
*
* @var object
*/
protected $mem = null;
/**
* memcache key
*
* @var string
*/
protected $memKey = '';
/**
* memcache active time
*
* @var int
*/
protected $memTime = 0;
/**
* log
*
* @var boolean
*/
protected $log = false;
protected $logName = __CLASS__; /**
* 是否执行成功
*
* @var bool
*/
protected $success = false; /**
* 构造函数
*/
public function __construct($dbname = '')
{
if (!empty($dbname)) {
$this->database($dbname);
} // 使用惰性连接
//$this->connect();
} /*
* 选择数据库
*/
public function database($dbname)
{
switch ($dbname) {
case 'tongzhuo_ask_db':
$this->selectAskHost();
break;
case 'tongzhuonet_db':
$this->selectTznetHost();
break;
case 'tongzhuo_weixin_db':
$this->selectWeixinHost();
break;
case 'tongzhuo_user_db':
$this->selectUserHost();
break;
case 'tongzhuo_examination_db':
$this->selectExaminationHost();
break;
case 'tongzhuo_tv_db':
$this->selectTvHost();
break;
case 'tongzhuo_db':
$this->selectTzHost();
break;
default:
throw new Exception('Not found database!');
}
return $this;
} /*
* 使用memcache
*/
public function mem($key = '', $activeTime = 0)
{
global $mem;
$this->mem = $mem;
$this->memKey = $key;
$this->memTime = $activeTime;
return $this;
} /**
* 选择哪些列
*
* @param string|array $cols
* @return self
*/
public function select($cols = '*')
{
$this->type = 'SELECT';
if (!is_array($cols)) {
$cols = explode(',', $cols);
}
$this->cols($cols);
return $this;
} /**
* 从哪个表删除
*
* @param string $table
* @return self
*/
public function delete($table)
{
$this->type = 'DELETE';
$this->table = $this->quoteName($table);
$this->fromRaw($this->quoteName($table));
return $this;
} /**
* 从哪个表hash删除
*
* @param string $pre
* @param string $salt
* @return self
*/
public function deleteHash($pre, $salt)
{
$table = $this->hashTable($pre, $salt);
return $this->delete($table);
} /**
* 更新哪个表
*
* @param string $table
* @return self
*/
public function update($table)
{
$this->type = 'UPDATE';
$this->table = $this->quoteName($table);
return $this;
} /**
* 更新哪个hash表
*
* @param string $pre
* @param string $salt
* @return self
*/
public function updateHash($pre, $salt)
{
$table = $this->hashTable($pre, $salt);
return $this->update($table);
} /**
* 向哪个表插入
*
* @param string $table
* @return self
*/
public function insert($table)
{
$this->type = 'INSERT';
$this->table = $this->quoteName($table);
return $this;
} /**
* 向哪个hash表插入
*
* @param string $pre
* @param string $hash
* @return self
*/
public function insertHash($pre, $salt)
{
$table = $this->hashTable($pre, $salt);
return $this->insert($table);
} /**
* from 哪个表
*
* @param string $table
* @return self
*/
public function from($table)
{
return $this->fromRaw($this->quoteName($table));
} /**
* from 哪个hash表
*
* @param string $pre
* @param string $salt
* @return self
*/
public function fromHash($pre, $salt)
{
$table = $this->hashTable($pre, $salt);
return $this->from($table);
} /**
* from的表
*
* @param string $table
* @return self
*/
public function fromRaw($table)
{
$this->from[] = array($table);
$this->from_key++;
return $this;
} /**
*
* 子查询
*
* @param string $table
* @param string $name The alias name for the sub-select.
* @return self
*/
public function fromSubSelect($table, $name)
{
$this->from[] = array("($table) AS " . $this->quoteName($name));
$this->from_key++;
return $this;
} /**
* 增加 join 语句
*
* @param string $table
* @param string $cond
* @param string $type
* @return self
* @throws Exception
*/
public function join($table, $cond = null, $type = '')
{
return $this->joinInternal($type, $table, $cond);
} /**
* 增加 join 语句
*
* @param string $join inner, left, natural
* @param string $table
* @param string $cond
* @return self
* @throws Exception
*/
protected function joinInternal($join, $table, $cond = null)
{
if (!$this->from) {
throw new Exception('Cannot join() without from()');
} $join = strtoupper(ltrim("$join JOIN"));
$table = $this->quoteName($table);
$cond = $this->fixJoinCondition($cond);
$this->from[$this->from_key][] = rtrim("$join $table $cond");
return $this;
} /**
* left join
*
* @param string $table
* @param string $cond
* @return self
* @throws Exception
*/
public function leftJoin($table, $cond = null)
{
return $this->joinInternal('LEFT', $table, $cond);
} /**
* right join
*
* @param string $table
* @param string $cond
* @return self
* @throws Exception
*/
public function rightJoin($table, $cond = null)
{
return $this->joinInternal('RIGHT', $table, $cond);
} /**
* joinSubSelect
*
* @param string $join inner, left, natural
* @param string $spec
* @param string $name sub-select 的别名
* @param string $cond
* @return self
* @throws Exception
*/
public function joinSubSelect($join, $spec, $name, $cond = null)
{
if (!$this->from) {
throw new Exception('Cannot join() without from() first.');
} $join = strtoupper(ltrim("$join JOIN"));
$name = $this->quoteName($name);
$cond = $this->fixJoinCondition($cond);
$this->from[$this->from_key][] = rtrim("$join ($spec) AS $name $cond");
return $this;
} /**
* group by 语句
*
* @param array $cols
* @return self
*/
public function groupBy(array $cols)
{
foreach ($cols as $col) {
$this->group_by[] = $this->quoteNamesIn($col);
}
return $this;
} /**
* having 语句
*
* @param string $cond
* @return self
*/
public function having($cond)
{
$this->addClauseCondWithBind('having', 'AND', func_get_args());
return $this;
} /**
* or having 语句
*
* @param string $cond The HAVING condition.
* @return self
*/
public function orHaving($cond)
{
$this->addClauseCondWithBind('having', 'OR', func_get_args());
return $this;
} /*
* 重连tv读写数据库
*/
protected function selectTvHost()
{
$this->readSettings['user'] = $GLOBALS['user_tv_read'];
$this->readSettings['password'] = $GLOBALS['pw_tv_read'];
$this->readSettings['host'] = $GLOBALS['host_tv_read'];
$this->readSettings['port'] = $GLOBALS['port_tv_read'];
$this->readSettings['dbname'] = $GLOBALS['database_tv_read'];
$this->writeSettings['user'] = $GLOBALS['user_tv_write'];
$this->writeSettings['password'] = $GLOBALS['pw_tv_write'];
$this->writeSettings['host'] = $GLOBALS['host_tv_write'];
$this->writeSettings['port'] = $GLOBALS['port_tv_write'];
$this->writeSettings['dbname'] = $GLOBALS['database_tv_write'];
} /*
* 重连ask读写数据库
*/
protected function selectAskHost()
{
$this->readSettings['user'] = $GLOBALS['user_ask_read'];
$this->readSettings['password'] = $GLOBALS['pw_ask_read'];
$this->readSettings['host'] = $GLOBALS['host_ask_read'];
$this->readSettings['port'] = $GLOBALS['port_ask_read'];
$this->readSettings['dbname'] = $GLOBALS['database_ask_read'];
$this->writeSettings['user'] = $GLOBALS['user_ask_write'];
$this->writeSettings['password'] = $GLOBALS['pw_ask_write'];
$this->writeSettings['host'] = $GLOBALS['host_ask_write'];
$this->writeSettings['port'] = $GLOBALS['port_ask_write'];
$this->writeSettings['dbname'] = $GLOBALS['database_ask_write'];
}
/*
* 重连net读写数据库
*/
protected function selectTznetHost()
{
$this->readSettings['user'] = $GLOBALS['user_tz_read'];
$this->readSettings['password'] = $GLOBALS['pw_tz_read'];
$this->readSettings['host'] = $GLOBALS['host_tz_read'];
$this->readSettings['port'] = $GLOBALS['port_tz_read'];
$this->readSettings['dbname'] = $GLOBALS['database_tz_read'];
$this->writeSettings['user'] = $GLOBALS['user_tz_write'];
$this->writeSettings['password'] = $GLOBALS['pw_tz_write'];
$this->writeSettings['host'] = $GLOBALS['host_tz_write'];
$this->writeSettings['port'] = $GLOBALS['port_tz_write'];
$this->writeSettings['dbname'] = $GLOBALS['database_tz_write'];
}
/*
* 重连weixin读写数据库
*/
protected function selectWeixinHost()
{
$this->readSettings['user'] = $GLOBALS['user_wx_read'];
$this->readSettings['password'] = $GLOBALS['pw_wx_read'];
$this->readSettings['host'] = $GLOBALS['host_wx_read'];
$this->readSettings['port'] = $GLOBALS['port_wx_read'];
$this->readSettings['dbname'] = $GLOBALS['database_wx_read'];
$this->writeSettings['user'] = $GLOBALS['user_wx_write'];
$this->writeSettings['password'] = $GLOBALS['pw_wx_write'];
$this->writeSettings['host'] = $GLOBALS['host_wx_write'];
$this->writeSettings['port'] = $GLOBALS['port_wx_write'];
$this->writeSettings['dbname'] = $GLOBALS['database_wx_write'];
}
/*
* 重连用户日志读写数据库
*/
protected function selectUserHost()
{
$this->readSettings['user'] = $GLOBALS['user_user_read'];
$this->readSettings['password'] = $GLOBALS['pw_user_read'];
$this->readSettings['host'] = $GLOBALS['host_user_read'];
$this->readSettings['port'] = $GLOBALS['port_user_read'];
$this->readSettings['dbname'] = $GLOBALS['database_user_read'];
$this->writeSettings['user'] = $GLOBALS['user_user_write'];
$this->writeSettings['password'] = $GLOBALS['pw_user_write'];
$this->writeSettings['host'] = $GLOBALS['host_user_write'];
$this->writeSettings['port'] = $GLOBALS['port_user_write'];
$this->writeSettings['dbname'] = $GLOBALS['database_user_write'];
}
/*
* 模拟考试读写数据库
*/
protected function selectExaminationHost()
{
$this->readSettings['user'] = $GLOBALS['user_ex_read'];
$this->readSettings['password'] = $GLOBALS['pw_ex_read'];
$this->readSettings['host'] = $GLOBALS['host_ex_read'];
$this->readSettings['port'] = $GLOBALS['port_ex_read'];
$this->readSettings['dbname'] = $GLOBALS['database_ex_read'];
$this->writeSettings['user'] = $GLOBALS['user_user_write'];
$this->writeSettings['password'] = $GLOBALS['pw_ex_write'];
$this->writeSettings['host'] = $GLOBALS['host_ex_write'];
$this->writeSettings['port'] = $GLOBALS['port_ex_write'];
$this->writeSettings['dbname'] = $GLOBALS['database_ex_write'];
}
/*
* 重连tongzhuo读写数据库
*/
protected function selectTzHost()
{
$this->readSettings['user'] = $GLOBALS['user_one_read'];
$this->readSettings['password'] = $GLOBALS['pw_one_read'];
$this->readSettings['host'] = $GLOBALS['host_one_read'];
$this->readSettings['port'] = $GLOBALS['port_one_read'];
$this->readSettings['dbname'] = $GLOBALS['database_one_read'];
$this->writeSettings['user'] = $GLOBALS['user_one_write'];
$this->writeSettings['password'] = $GLOBALS['pw_one_write'];
$this->writeSettings['host'] = $GLOBALS['host_one_write'];
$this->writeSettings['port'] = $GLOBALS['port_one_write'];
$this->writeSettings['dbname'] = $GLOBALS['database_one_write'];
} /**
* 创建 PDO 实例
*/
protected function connect()
{
if ($this->type == 'SELECT') {
if (
empty($this->readSettings['dbname']) &&
empty($this->readSettings['host']) &&
empty($this->readSettings['port']) &&
empty($this->readSettings['user']) &&
empty($this->readSettings['password'])) {
throw new Exception('Database configuration fail!');
}
$dsn = 'mysql:dbname=' . $this->readSettings['dbname'] .
';host=' . $this->readSettings['host'] . ';port=' . $this->readSettings['port'] .
';charset=utf8';
$this->pdo = new PDO(
$dsn,
$this->readSettings['user'],
$this->readSettings['password']
);
} else {
if (
empty($this->writeSettings['dbname']) &&
empty($this->writeSettings['host']) &&
empty($this->writeSettings['port']) &&
empty($this->writeSettings['user']) &&
empty($this->writeSettings['password'])) {
throw new Exception('Database configuration fail!');
}
$dsn = 'mysql:dbname=' . $this->writeSettings['dbname'] .
';host=' . $this->writeSettings['host'] . ';port=' . $this->writeSettings['port'] .
';charset=utf8';
$this->pdo = new PDO(
$dsn,
$this->writeSettings['user'],
$this->writeSettings['password']
);
}
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} /*
* 清空本次mem设置信息
*/
protected function resetMem()
{
$this->mem = null;
$this->memKey = '';
$this->memTime = 0;
} /**
* 关闭连接
*/
public function closeConnection()
{
$this->pdo = null;
}
/**
*
* 设置 SQL_CALC_FOUND_ROWS 标记.
*
* @param bool $enable
* @return self
*/
public function calcFoundRows($enable = true)
{
$this->setFlag('SQL_CALC_FOUND_ROWS', $enable);
return $this;
} /**
* 设置 SQL_CACHE 标记
*
* @param bool $enable
* @return self
*/
public function cache($enable = true)
{
$this->setFlag('SQL_CACHE', $enable);
return $this;
} /**
* 设置 SQL_NO_CACHE 标记
*
* @param bool $enable
* @return self
*/
public function noCache($enable = true)
{
$this->setFlag('SQL_NO_CACHE', $enable);
return $this;
} /**
* 设置 STRAIGHT_JOIN 标记.
*
* @param bool $enable
* @return self
*/
public function straightJoin($enable = true)
{
$this->setFlag('STRAIGHT_JOIN', $enable);
return $this;
} /**
* 设置 HIGH_PRIORITY 标记
*
* @param bool $enable
* @return self
*/
public function highPriority($enable = true)
{
$this->setFlag('HIGH_PRIORITY', $enable);
return $this;
} /**
* 设置 SQL_SMALL_RESULT 标记
*
* @param bool $enable
* @return self
*/
public function smallResult($enable = true)
{
$this->setFlag('SQL_SMALL_RESULT', $enable);
return $this;
} /**
* 设置 SQL_BIG_RESULT 标记
*
* @param bool $enable
* @return self
*/
public function bigResult($enable = true)
{
$this->setFlag('SQL_BIG_RESULT', $enable);
return $this;
} /**
* 设置 SQL_BUFFER_RESULT 标记
*
* @param bool $enable
* @return self
*/
public function bufferResult($enable = true)
{
$this->setFlag('SQL_BUFFER_RESULT', $enable);
return $this;
} /**
* 设置 DISTINCT 标记
*
* @param bool $enable
* @return self
*/
public function distinct($enable = true)
{
$this->setFlag('DISTINCT', $enable);
return $this;
} /**
* 设置 LOW_PRIORITY 标记
*
* @param bool $enable
* @return self
*/
public function lowPriority($enable = true)
{
$this->setFlag('LOW_PRIORITY', $enable);
return $this;
} /**
* 设置 IGNORE 标记
*
* @param bool $enable
* @return self
*/
public function ignore($enable = true)
{
$this->setFlag('IGNORE', $enable);
return $this;
} /**
* 设置 QUICK 标记
*
* @param bool $enable
* @return self
*/
public function quick($enable = true)
{
$this->setFlag('QUICK', $enable);
return $this;
} /**
* 设置 DELAYED 标记
*
* @param bool $enable
* @return self
*/
public function delayed($enable = true)
{
$this->setFlag('DELAYED', $enable);
return $this;
} /**
* 设置 FOR UPDATE 标记
*
* @param bool $enable
* @return self
*/
public function forUpdate($enable = true)
{
$this->for_update = (bool) $enable;
return $this;
} /**
* 设置每页多少条记录
*
* @param int $paging
* @return self
*/
public function setPaging($paging)
{
$this->paging = (int) $paging;
return $this;
} /**
* 获取每页多少条记录
*
* @return int
*/
public function getPaging()
{
return $this->paging;
} /**
* 序列化
*
* @return string
*/
public function __toString()
{
$union = '';
if ($this->union) {
$union = implode(' ', $this->union) . ' ';
}
return $union . $this->build();
} /**
* 获取绑定在占位符上的值
*/
public function getBindValues()
{
switch ($this->type) {
case 'SELECT':
return $this->getBindValuesSELECT();
case 'DELETE':
case 'UPDATE':
case 'INSERT':
return $this->getBindValuesCOMMON();
default:
throw new Exception("type err");
}
} /**
* 获取绑定在占位符上的值
*
* @return array
*/
public function getBindValuesSELECT()
{
$bind_values = $this->bind_values;
$i = 1;
foreach ($this->bind_where as $val) {
$bind_values[$i] = $val;
$i++;
}
foreach ($this->bind_having as $val) {
$bind_values[$i] = $val;
$i++;
}
return $bind_values;
} /**
*
* SELECT选择哪些列
*
* @param mixed $key
* @param string $val
* @return void
*/
protected function addColSELECT($col, $alias)
{
if (is_string($col)) {
$this->cols[$alias] = $col;
} else {
$this->addColWithAlias($alias);
}
} /**
* SELECT 增加选择的列
*
* @param string $spec
*/
protected function addColWithAlias($spec)
{
$parts = explode(' ', trim($spec));
$count = count($parts);
if ($count == 2) {
$this->cols[$parts[1]] = $parts[0];
} elseif ($count == 3 && strtoupper($parts[1]) == 'AS') {
$this->cols[$parts[2]] = $parts[0];
} else {
$this->cols[] = $spec;
}
} /**
* quote
*
* @param string $cond
* @return string
*
*/
protected function fixJoinCondition($cond)
{
if (!$cond) {
return '';
} $cond = $this->quoteNamesIn($cond); if (strtoupper(substr(ltrim($cond), 0, 3)) == 'ON ') {
return $cond;
} if (strtoupper(substr(ltrim($cond), 0, 6)) == 'USING ') {
return $cond;
} return 'ON ' . $cond;
} /**
* inner join
*
* @param string $table
* @param string $cond
* @return self
* @throws Exception
*/
public function innerJoin($table, $cond = null)
{
return $this->joinInternal('INNER', $table, $cond);
}
/**
* 设置每页的记录数量
*
* @param int $page
* @return self
*/
public function page($page)
{
$this->limit = 0;
$this->offset = 0; $page = (int) $page;
if ($page > 0) {
$this->limit = $this->paging;
$this->offset = $this->paging * ($page - 1);
}
return $this;
} /**
* union
*
* @return self
*/
public function union()
{
$this->union[] = $this->build() . ' UNION';
$this->reset();
return $this;
} /**
* unionAll
*
* @return self
*/
public function unionAll()
{
$this->union[] = $this->build() . ' UNION ALL';
$this->reset();
return $this;
} /**
* 重置
*/
protected function reset()
{
$this->resetFlags();
$this->cols = array();
$this->from = array();
$this->from_key = -1;
$this->where = array();
$this->group_by = array();
$this->having = array();
$this->order_by = array();
$this->limit = 0;
$this->offset = 0;
$this->for_update = false;
} /**
* 清除所有数据
*/
protected function resetAll()
{
$this->union = array();
$this->for_update = false;
$this->cols = array();
$this->from = array();
$this->from_key = -1;
$this->group_by = array();
$this->having = array();
$this->bind_having = array();
$this->paging = 10;
$this->bind_values = array();
$this->where = array();
$this->bind_where = array();
$this->order_by = array();
$this->limit = 0;
$this->offset = 0;
$this->flags = array();
$this->table = '';
$this->last_insert_id_names = array();
$this->col_values = array();
$this->returning = array();
$this->parameters = array();
} /**
* 创建 SELECT SQL
*
* @return string
*/
protected function buildSELECT()
{
return 'SELECT'
. $this->buildFlags()
. $this->buildCols()
. $this->buildFrom()
. $this->buildWhere()
. $this->buildGroupBy()
. $this->buildHaving()
. $this->buildOrderBy()
. $this->buildLimit()
. $this->buildForUpdate();
} /**
* 创建 DELETE SQL
*/
protected function buildDELETE()
{
return 'DELETE'
. $this->buildFlags()
. $this->buildFrom()
. $this->buildWhere()
. $this->buildOrderBy()
. $this->buildLimit()
. $this->buildReturning();
} /**
* 生成 SELECT 列语句
*
* @return string
* @throws Exception
*/
protected function buildCols()
{
if (!$this->cols) {
throw new Exception('No columns in the SELECT.');
} $cols = array();
foreach ($this->cols as $key => $val) {
if (is_int($key)) {
$cols[] = $this->quoteNamesIn($val);
} else {
$cols[] = $this->quoteNamesIn("$val AS $key");
}
} return $this->indentCsv($cols);
} /**
* 生成 FROM 语句.
*
* @return string
*/
protected function buildFrom()
{
if (!$this->from) {
return '';
} $refs = array();
foreach ($this->from as $from) {
$refs[] = implode(' ', $from);
}
return ' FROM' . $this->indentCsv($refs);
} /**
* 生成 GROUP BY 语句.
*
* @return string
*/
protected function buildGroupBy()
{
if (!$this->group_by) {
return '';
}
return ' GROUP BY' . $this->indentCsv($this->group_by);
} /**
* 生成 HAVING 语句.
*
* @return string
*/
protected function buildHaving()
{
if (!$this->having) {
return '';
}
return ' HAVING' . $this->indent($this->having);
} /**
* 生成 FOR UPDATE 语句
*
* @return string
*/
protected function buildForUpdate()
{
if (!$this->for_update) {
return '';
}
return ' FOR UPDATE';
} /**
* where
*
* @param string|array $cond
* @return self
*/
public function where($cond)
{
if (is_array($cond)) {
foreach ($cond as $key => $val) {
if (is_string($key)) {
$this->addWhere('AND', array($key, $val));
} else {
$this->addWhere('AND', array($val));
}
}
} else {
$this->addWhere('AND', func_get_args());
}
return $this;
} /**
* or where
*
* @param string|array $cond
* @return self
*/
public function orWhere($cond)
{
if (is_array($cond)) {
foreach ($cond as $key => $val) {
if (is_string($key)) {
$this->addWhere('OR', array($key, $val));
} else {
$this->addWhere('OR', array($val));
}
}
} else {
$this->addWhere('OR', func_get_args());
}
return $this;
} /**
* limit
*
* @param int $limit
* @return self
*/
public function limit($limit)
{
$this->limit = (int) $limit;
return $this;
} /**
* limit offset
*
* @param int $offset
* @return self
*/
public function offset($offset)
{
$this->offset = (int) $offset;
return $this;
} /**
* orderby.
*
* @param array $cols
* @return self
*/
public function orderBy(array $cols)
{
return $this->addOrderBy($cols);
} /**
* order by ASC OR DESC
*
* @param array $cols
* @param bool $order_asc
* @return self
*/
public function orderByASC(array $cols, $order_asc = true)
{
$this->order_asc = $order_asc;
return $this->addOrderBy($cols);
} /**
* order by DESC
*
* @param array $cols
* @return self
*/
public function orderByDESC(array $cols)
{
$this->order_asc = false;
return $this->addOrderBy($cols);
} // -------------abstractquery----------
/**
* 返回逗号分隔的字符串
*
* @param array $list
* @return string
*/
protected function indentCsv(array $list)
{
return ' ' . implode(',', $list);
} /**
* 返回空格分隔的字符串
*
* @param array $list
* @return string
*/
protected function indent(array $list)
{
return ' ' . implode(' ', $list);
} /**
* 批量为占位符绑定值
*
* @param array $bind_values
* @return self
*
*/
public function bindValues(array $bind_values)
{
foreach ($bind_values as $key => $val) {
$this->bindValue($key, $val);
}
return $this;
} /**
* 单个为占位符绑定值
*
* @param string $name
* @param mixed $value
* @return self
*/
public function bindValue($name, $value)
{
$this->bind_values[$name] = $value;
return $this;
} /**
* 生成 flag
*
* @return string
*/
protected function buildFlags()
{
if (!$this->flags) {
return '';
}
return ' ' . implode(' ', array_keys($this->flags));
} /**
* 设置 flag.
*
* @param string $flag
* @param bool $enable
*/
protected function setFlag($flag, $enable = true)
{
if ($enable) {
$this->flags[$flag] = true;
} else {
unset($this->flags[$flag]);
}
} /**
* 重置 flag
*/
protected function resetFlags()
{
$this->flags = array();
} /**
*
* 添加 where 语句
*
* @param string $andor 'AND' or 'OR
* @param array $conditions
* @return self
*
*/
protected function addWhere($andor, $conditions)
{
$this->addClauseCondWithBind('where', $andor, $conditions);
return $this;
} /**
* 添加条件和绑定值
*
* @param string $clause where 、having等
* @param string $andor AND、OR等
* @param array $conditions
*/
protected function addClauseCondWithBind($clause, $andor, $conditions)
{
$cond = array_shift($conditions);
$cond = $this->quoteNamesIn($cond); $bind = &$this->{"bind_{$clause}"};
foreach ($conditions as $value) {
$bind[] = $value;
} $clause = &$this->$clause;
if ($clause) {
$clause[] = "$andor $cond";
} else {
$clause[] = $cond;
}
} /**
* 生成 where 语句
*
* @return string
*/
protected function buildWhere()
{
if (!$this->where) {
return '';
}
return ' WHERE' . $this->indent($this->where);
} /**
* 增加 order by
*
* @param array $spec The columns and direction to order by.
* @return self
*/
protected function addOrderBy(array $spec)
{
foreach ($spec as $col) {
$this->order_by[] = $this->quoteNamesIn($col);
}
return $this;
} /**
* 生成 order by 语句
*
* @return string
*/
protected function buildOrderBy()
{
if (!$this->order_by) {
return '';
} if ($this->order_asc) {
return ' ORDER BY' . $this->indentCsv($this->order_by) . ' ASC';
} else {
return ' ORDER BY' . $this->indentCsv($this->order_by) . ' DESC';
}
} /**
* 生成 limit 语句
*
* @return string
*/
protected function buildLimit()
{
$has_limit = $this->type == 'DELETE' || $this->type == 'UPDATE';
$has_offset = $this->type == 'SELECT'; if ($has_offset && $this->limit) {
$clause = " LIMIT {$this->limit}";
if ($this->offset) {
$clause .= " OFFSET {$this->offset}";
}
return $clause;
} elseif ($has_limit && $this->limit) {
return " LIMIT {$this->limit}";
}
return '';
} /**
* Quotes
*
* @param string $spec
* @return string|array
*/
public function quoteName($spec)
{
$spec = trim($spec);
$seps = array(' AS ', ' ', '.');
foreach ($seps as $sep) {
$pos = strripos($spec, $sep);
if (false !== $pos) {
return $this->quoteNameWithSeparator($spec, $sep, $pos);
}
}
return $this->replaceName($spec);
} /**
* 指定分隔符的 Quotes
*
* @param string $spec
* @param string $sep
* @param int $pos
* @return string
*/
protected function quoteNameWithSeparator($spec, $sep, $pos)
{
$len = strlen($sep);
$part1 = $this->quoteName(substr($spec, 0, $pos));
$part2 = $this->replaceName(substr($spec, $pos + $len));
return "{$part1}{$sep}{$part2}";
} /**
* Quotes "table.col" 格式的字符串
*
* @param string $text
* @return string|array
*/
public function quoteNamesIn($text)
{
$list = $this->getListForQuoteNamesIn($text);
$last = count($list) - 1;
$text = null;
foreach ($list as $key => $val) {
if (($key + 1) % 3) {
$text .= $this->quoteNamesInLoop($val, $key == $last);
}
}
return $text;
} /**
* 返回 quote 元素列表
*
* @param string $text
* @return array
*/
protected function getListForQuoteNamesIn($text)
{
$apos = "'";
$quot = '"';
return preg_split(
"/(($apos+|$quot+|\\$apos+|\\$quot+).*?\\2)/",
$text,
-1,
PREG_SPLIT_DELIM_CAPTURE
);
} /**
* 循环 quote
*
* @param string $val
* @param bool $is_last
* @return string
*/
protected function quoteNamesInLoop($val, $is_last)
{
if ($is_last) {
return $this->replaceNamesAndAliasIn($val);
}
return $this->replaceNamesIn($val);
} /**
* 替换成别名
*
* @param string $val
* @return string
*/
protected function replaceNamesAndAliasIn($val)
{
$quoted = $this->replaceNamesIn($val);
$pos = strripos($quoted, ' AS ');
if ($pos !== false) {
$alias = $this->replaceName(substr($quoted, $pos + 4));
$quoted = substr($quoted, 0, $pos) . " AS $alias";
}
return $quoted;
} /**
* Quotes name
*
* @param string $name
* @return string
*/
protected function replaceName($name)
{
$name = trim($name);
if ($name == '*') {
return $name;
}
return '`' . $name . '`';
} /**
* Quotes
*
* @param string $text
* @return string|array
*/
protected function replaceNamesIn($text)
{
$is_string_literal = strpos($text, "'") !== false
|| strpos($text, '"') !== false;
if ($is_string_literal) {
return $text;
} $word = '[a-z_][a-z0-9_]*'; $find = "/(\\b)($word)\\.($word)(\\b)/i"; $repl = '$1`$2`.`$3`$4'; $text = preg_replace($find, $repl, $text); return $text;
} // ---------- insert --------------
/**
* 设置 `table.column` 与 last-insert-id 的映射
*
* @param array $last_insert_id_names
*/
public function setLastInsertIdNames(array $last_insert_id_names)
{
$this->last_insert_id_names = $last_insert_id_names;
} /**
* insert into.
*
* @param string $table
* @return self
*/
public function into($table)
{
$this->table = $this->quoteName($table);
return $this;
} /**
* 生成 INSERT 语句
*
* @return string
*/
protected function buildINSERT()
{
return 'INSERT'
. $this->buildFlags()
. $this->buildInto()
. $this->buildValuesForInsert()
. $this->buildReturning();
} /**
* 生成 INTO 语句
*
* @return string
*/
protected function buildInto()
{
return " INTO " . $this->table;
} /**
* PDO::lastInsertId()
*
* @param string $col
* @return mixed
*/
public function getLastInsertIdName($col)
{
$key = str_replace('`', '', $this->table) . '.' . $col;
if (isset($this->last_insert_id_names[$key])) {
return $this->last_insert_id_names[$key];
} return null;
} /**
* 设置一列,如果有第二各参数,则把第二个参数绑定在占位符上
*
* @param string $col
* @return self
*/
public function col($col)
{
return call_user_func_array(array($this, 'addCol'), func_get_args());
} /**
* 设置多列
*
* @param array $cols
* @return self
*/
public function cols(array $cols)
{
if ($this->type == 'SELECT') {
foreach ($cols as $key => $val) {
$this->addColSELECT($key, $val);
}
return $this;
}
return $this->addCols($cols);
} /**
* 直接设置列的值
*
* @param string $col
* @param string $value
* @return self
*/
public function set($col, $value)
{
return $this->setCol($col, $value);
} /**
* 为 INSERT 语句绑定值
*
* @return string
*/
protected function buildValuesForInsert()
{
return ' (' . $this->indentCsv(array_keys($this->col_values)) . ') VALUES (' .
$this->indentCsv(array_values($this->col_values)) . ')';
} // ------update-------
/**
* 更新哪个表
*
* @param string $table
* @return self
*/
public function table($table)
{
$this->table = $this->quoteName($table);
return $this;
} /**
* 生成完整 SQL 语句
*
* @return string
* @throws Exception
*/
protected function build()
{
switch ($this->type) {
case 'DELETE':
return $this->buildDELETE();
case 'INSERT':
return $this->buildINSERT();
case 'UPDATE':
return $this->buildUPDATE();
case 'SELECT':
return $this->buildSELECT();
}
throw new Exception("type empty");
} /**
* 生成更新的 SQL 语句
*/
protected function buildUPDATE()
{
return 'UPDATE'
. $this->buildFlags()
. $this->buildTable()
. $this->buildValuesForUpdate()
. $this->buildWhere()
. $this->buildOrderBy()
. $this->buildLimit()
. $this->buildReturning();
} /**
* 哪个表
*
* @return string
*/
protected function buildTable()
{
return " {$this->table}";
} /**
* 为更新语句绑定值
*
* @return string
*/
protected function buildValuesForUpdate()
{
$values = array();
foreach ($this->col_values as $col => $value) {
$values[] = "{$col} = {$value}";
}
return ' SET' . $this->indentCsv($values);
} // ----------Dml---------------
/**
* 获取绑定的值
*
* @return array
*/
public function getBindValuesCOMMON()
{
$bind_values = $this->bind_values;
$i = 1;
foreach ($this->bind_where as $val) {
$bind_values[$i] = $val;
$i++;
}
return $bind_values;
} /**
* 设置列
*
* @param string $col
* @return self
*/
protected function addCol($col)
{
$key = $this->quoteName($col);
$this->col_values[$key] = ":$col";
$args = func_get_args();
if (count($args) > 1) {
$this->bindValue($col, $args[1]);
}
return $this;
} /**
* 设置多个列
*
* @param array $cols
* @return self
*/
protected function addCols(array $cols)
{
foreach ($cols as $key => $val) {
if (is_int($key)) {
$this->addCol($val);
} else {
$this->addCol($key, $val);
}
}
return $this;
} /**
* 设置单列的值
*
* @param string $col .
* @param string $value
* @return self
*/
protected function setCol($col, $value)
{
if ($value === null) {
$value = 'NULL';
} $key = $this->quoteName($col);
$value = $this->quoteNamesIn($value);
$this->col_values[$key] = $value;
return $this;
} /**
* 增加返回的列
*
* @param array $cols
* @return self
*
*/
protected function addReturning(array $cols)
{
foreach ($cols as $col) {
$this->returning[] = $this->quoteNamesIn($col);
}
return $this;
} /**
* 生成 RETURNING 语句
*
* @return string
*/
protected function buildReturning()
{
if (!$this->returning) {
return '';
}
return ' RETURNING' . $this->indentCsv($this->returning);
} /**
* 执行
*
* @param string $query
* @param string $parameters
* @throws PDOException
*/
protected function execute($query, $parameters = "")
{
try {
$this->connect();
$this->sQuery = @$this->pdo->prepare($query);
$this->bindMore($parameters);
if (!empty($this->parameters)) {
foreach ($this->parameters as $param) {
$parameters = explode("\x7F", $param);
$this->sQuery->bindParam($parameters[0], $parameters[1]);
}
}
$this->success = $this->sQuery->execute();
} catch (PDOException $e) {
// 服务端断开时重连一次
if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
$this->closeConnection();
try {
$this->connect();
$this->sQuery = $this->pdo->prepare($query);
$this->bindMore($parameters);
if (!empty($this->parameters)) {
foreach ($this->parameters as $param) {
$parameters = explode("\x7F", $param);
$this->sQuery->bindParam($parameters[0], $parameters[1]);
}
}
$this->success = $this->sQuery->execute();
} catch (PDOException $ex) {
$this->rollBackTrans();
throw $ex;
}
} else {
$this->rollBackTrans();
$msg = $e->getMessage();
$err_msg = "SQL:" . $this->lastSQL() . " " . $msg;
$exception = new PDOException($err_msg, (int) $e->getCode());
throw $exception;
}
}
if ($this->log) {
$message = sprintf("%s|%s", $query, implode(',', $this->parameters));
comm_log($this->logName, $message);
}
$this->parameters = array();
} /**
* 绑定
*
* @param string $para
* @param string $value
*/
public function bind($para, $value)
{
if (is_string($para)) {
$this->parameters[sizeof($this->parameters)] = ":" . $para . "\x7F" . $value;
} else {
$this->parameters[sizeof($this->parameters)] = $para . "\x7F" . $value;
}
} /**
* 绑定多个
*
* @param array $parray
*/
public function bindMore($parray)
{
if (empty($this->parameters) && is_array($parray)) {
$columns = array_keys($parray);
foreach ($columns as $i => &$column) {
$this->bind($column, $parray[$column]);
}
}
} /**
* 执行 SQL
*
* @param string $query
* @param array $params
* @param int $fetchmode
* @return mixed
*/
public function query($query = '', $params = null, $fetchmode = PDO::FETCH_ASSOC)
{
$query = trim($query);
if (empty($query)) {
$query = $this->build();
if (!$params) {
$params = $this->getBindValues();
}
} else {
$this->parseType($query);
}
$this->resetAll();
$this->lastSql = $query;
if ($this->type == 'SELECT' && $this->mem) {
if (empty($this->memKey)) {
$this->memKey = $query . ' [' . implode(',', $params) . ']';
} $buf = $this->mem->get($this->memKey);
if ($buf !== false) {
$this->resetMem();
return $buf;
}
}
$this->execute($query, $params);
$rawStatement = explode(" ", $query);
$statement = strtolower(trim($rawStatement[0]));
if ($statement === 'select' || $statement === 'show') {
$rs = $this->sQuery->fetchAll($fetchmode);
if ($statement === 'select' && $this->mem) {
$this->mem->set($this->memKey, $rs, 0, $this->memTime);
$this->resetMem();
}
return $rs;
} elseif ($statement === 'update' || $statement === 'delete') {
return $this->sQuery->rowCount();
} elseif ($statement === 'insert') {
if ($this->sQuery->rowCount() > 0) {
return $this->lastInsertId();
}
} else {
return null;
} return null;
} /**
* 返回一列
*
* @param string $query
* @param array $params
* @return array
*/
public function column($query = '', $params = null)
{
$query = trim($query);
if (empty($query)) {
$query = $this->build();
if (!$params) {
$params = $this->getBindValues();
}
}
$this->resetAll();
$this->lastSql = $query;
if ($this->mem) {
if (empty($this->memKey)) {
$this->memKey = $query . ' [' . implode(',', $params) . ']';
} $buf = $this->mem->get($this->memKey);
if ($buf !== false) {
$this->resetMem();
return $buf;
}
}
if (empty($this->type)) {
$this->parseType($query);
}
$this->execute($query, $params);
$columns = $this->sQuery->fetchAll(PDO::FETCH_NUM);
$column = null;
foreach ($columns as $cells) {
$column[] = $cells[0];
}
if ($this->mem) {
$this->mem->set($this->memKey, $column, 0, $this->memTime);
$this->resetMem();
}
return $column;
} /**
* 返回一行
*
* @param string $query
* @param array $params
* @param int $fetchmode
* @return array
*/
public function row($query = '', $params = null, $fetchmode = PDO::FETCH_ASSOC)
{
$query = trim($query);
if (empty($query)) {
$query = $this->build();
if (!$params) {
$params = $this->getBindValues();
}
} $this->resetAll();
$this->lastSql = $query;
if ($this->mem) {
if (empty($this->memKey)) {
$this->memKey = $query . ' [' . implode(',', $params) . ']';
}
$buf = $this->mem->get($this->memKey);
if ($buf !== false) {
$this->resetMem();
return $buf;
}
}
if (empty($this->type)) {
$this->parseType($query);
}
$this->execute($query, $params);
$rs = $this->sQuery->fetch($fetchmode);
if ($this->mem) {
$this->mem->set($this->memKey, $rs, 0, $this->memTime);
$this->resetMem();
}
return $rs;
} /**
* 返回单个值
*
* @param string $query
* @param array $params
* @return string
*/
public function single($query = '', $params = null)
{
$query = trim($query);
if (empty($query)) {
$query = $this->build();
if (!$params) {
$params = $this->getBindValues();
}
}
$this->resetAll();
if ($this->mem) {
if (empty($this->memKey)) {
$this->memKey = $query . ' [' . implode(',', $params) . ']';
}
$buf = $this->mem->get($this->memKey);
if ($buf !== false) {
$this->resetMem();
return $buf;
}
}
if (empty($this->type)) {
$this->parseType($query);
}
$this->execute($query, $params);
$rs = $this->sQuery->fetchColumn();
if ($this->mem) {
$this->mem->set($this->memKey, $rs, 0, $this->memTime);
$this->resetMem();
}
return $rs;
} /**
* 返回 lastInsertId
*
* @return string
*/
public function lastInsertId()
{
return $this->pdo->lastInsertId();
} /**
* 返回最后一条执行的 sql
*
* @return string
*/
public function lastSQL()
{
return $this->lastSql;
} /**
* 开始事务
*/
public function beginTrans()
{
try {
return $this->pdo->beginTransaction();
} catch (PDOException $e) {
// 服务端断开时重连一次
if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
$this->closeConnection();
$this->connect();
return $this->pdo->beginTransaction();
} else {
throw $e;
}
}
} /**
* 提交事务
*/
public function commitTrans()
{
return $this->pdo->commit();
} /**
* 事务回滚
*/
public function rollBackTrans()
{
if ($this->pdo) {
if ($this->pdo->inTransaction()) {
return $this->pdo->rollBack();
}
}
return true;
} /**
* hash分表
*/
protected function hashTable($pre, $salt)
{
if (empty($salt)) {
return $pre . '0';
} $salt = strtolower($salt);
if (is_numeric($salt) && strlen($salt) > 1) {
$hash = substr($salt, -2, 2);
} else if (preg_match("/^[0-9a-z]{1}$/", substr($salt, -1, 1))) {
$hash = substr($salt, -1, 1);
} else {
return $pre . '0';
}
return $pre . $hash;
} public function log($logName = '')
{
$this->log = true;
if (!empty($logName)) {
$this->logName = $logName;
}
return $this;
} /**
* 没有设置type,也不是select(),update(),insert(),delete()方法,则分析sql语句,设置type
*/
public function parseType($query)
{
$query = trim($query);
if (empty($query)) {
$this->type = 'SELECT';
}
$this->type = strtoupper(explode(' ', $query)[0]);
}
/**
* (废弃,已自动判断)
* SELECT:读库
* 其他:写库
*/
public function setType($type) {
$this->type = strtoupper($type);
}
}

PDO连接数据库-Xmodel的更多相关文章

  1. 【转】php pdo连接数据库 解决中文乱码问题(wordpress mysql 问号?? ??)

    原文链接:http://blog.csdn.net/ysydao/article/details/11002295 PHP 用pdo连接数据库时出现中文乱码问题解决办法 1.array(PDO::MY ...

  2. PDO连接数据库

    PDO连接数据库 PDO简介和配置 php.ini extension=php_pdo.dll extension=php_pdo_myysql.dll PDO连接MYSQL new PDO(&quo ...

  3. 使用PDO连接数据库 查询和插入乱码的解决方法

    问题:PDO连接数据库后,查询和插入中文到数据库,出现乱码,如图: 解决方法: 法1: try{ $opts_values = array(PDO::MYSQL_ATTR_INIT_COMMAND=& ...

  4. PHP5中使用PDO连接数据库的方法

    PDO(PHP Data Object) 是PHP 中加入的东西,是PHP 5新加入的一个重大功能,因为在PHP 5以前的php4/php3都是一堆的数据库扩展来跟各个数据库的连接和处理,php_my ...

  5. php PDO连接数据库

    [PDO是啥] PDO是PHP 5新加入的一个重大功能,因为在PHP 5以前的php4/php3都是一堆的数据库扩展来跟各个数据库的连接和处理,什么 php_mysql.dll.php_pgsql.d ...

  6. php+mysql+pdo连接数据库

    1.$pdo = new PDO("mysql:host=localhost;dbname=test","root","123456");/ ...

  7. 封装PDO连接数据库代码

    废话不说直接上代码: <?php class DB { protected static $_connect; protected $dsn, $pdo; protected $_data, $ ...

  8. 使用PDO连接数据库

    PDO(PHP数据对象) 是一个轻量级的.具有兼容接口的PHP数据连接拓展,是一个PHP官方的PECL库,随PHP 5.1发布,需要PHP 5的面向对象支持,因而在更早的版本上无法使用.它所提供的数据 ...

  9. pdo 连接数据库 报错 could not find driver 解决方法

    在windows 下,调试一个PHP程序时,报了这个错误, could not find driver 原来我的这个程序中用到了PDO对象, 连接mysql 5.  在PHP的默认设置中,只打开了ph ...

随机推荐

  1. 阿里云安装nodejs和mongodb

    1) 安装nodejs wget https://nodejs.org/dist/v8.11.3/node-v8.11.3-linux-x64.tar.xz tar -xf node-v8.11.3- ...

  2. Python3 与 C# 并发编程之~ Net篇

    NetCore并发编程 示例代码:https://github.com/lotapp/BaseCode/tree/master/netcore/4_Concurrency 先简单说下概念(其实之前也有 ...

  3. 初始化bootstrap treeview树节点

    最近在做启明星图库时,使用了Jquery Bootstrap  Treeview插件.但是,遇到了一个初始化的问题.先看效果如下: 当用户打开图库时,左边分类第一个类别是“所有分类”,默认需要选中. ...

  4. I2C总线协议图解

    原帖地址:https://www.cnblogs.com/aaronLinux/p/6218660.html

  5. 浅谈MFC类CrackMe中消息处理函数查找方法

    最近一个学姐发给我了一份CrackMe希望我解一下,其中涉及到了MFC的消息函数查找的问题,就顺便以此为例谈一下自己使用的消息函数查找的方法.本人萌新,如果有任何错漏与解释不清的地方,欢迎各路大佬指正 ...

  6. jvm理论-class文件

    当JVM运行Java程序的时候,它会加载对应的class文件,并提取class文件中的信息存放在JVM的方法区内存中. Class文件组成 1.Class文件是一组以8位字节为基础单位的二进制流,各个 ...

  7. IIS 重写 HTTP 重定向到 HTTPS

    1.购买SSL证书 2.IIS7 / IIS 7.5 下绑定 HTTPS 网站(购买Wildcard SSL泛域名证书可绑定多个子域名)参考上文 3.下载安装URL重写模块:Microsoft URL ...

  8. iostat各字段的来源和真实含义

    The primary tool for inspecting Linux disk performance is iostat. The output includes many important ...

  9. mysqldump详解之--master-data

    在前一篇文章中,有提到mysqldump的--single-transaction参数.另外还有个很重要,也是运维中经常用到的参数:--master-data,网上很多关于MySQL不停机备份的实现都 ...

  10. Nodejs 使用 es module (import/export)