AbstractDao.class.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. <?php
  2. namespace KIF\Dao;
  3. use KIF\Exception\DaoException;
  4. use KIF\Verify;
  5. use KIF\Exception\ParamsException;
  6. use Exception;
  7. use KIF\Db\MySQLi;
  8. use KIF\Dao\SqlHelper;
  9. use KIF\Core\Config;
  10. use KIF\Cache\Memcached;
  11. use KIF\Math\Math;
  12. /**
  13. * 数据库操作的抽象类
  14. * @author gaoxiaogang@yoka.com
  15. *
  16. */
  17. abstract class AbstractDao {
  18. /**
  19. * 数据表名
  20. * 该抽象类针对表的操作(如:delete、create、findBy)都是基于该属性。由继承的子类填充该值。
  21. * 好处:1、如果继承的子类只针对一个表,把tableName设为静态表名即可。
  22. * 2、如果继承的子类针对一类表操作(如根据某个唯一标志把相同结构的数据hash到多张表中),则针对不同表操作时,动态设置该值
  23. * @var string
  24. */
  25. protected $tableName;
  26. /**
  27. * 表的主键名。默认为id
  28. * @var string
  29. */
  30. protected $primaryKey = 'id';
  31. /**
  32. *
  33. * 是否使用缓存
  34. * @var boolean
  35. */
  36. private $useCache = false;
  37. /**
  38. *
  39. * 缓存失效时间 ,默认1小时
  40. * @var int
  41. */
  42. private $cache_expiration = 3600;
  43. /**
  44. *
  45. * 存放这个数据表的缓存标志。
  46. * 目的:清空这个标志,能清空这张表的所有行记录缓存
  47. * @var string
  48. */
  49. private $tableCacheFlag;
  50. /**
  51. *
  52. * 用于缓存表记录的memcache缓存集群标志
  53. * 优先使用config里的 memcached->dao_cache;如不存在,则使用 memcached->default;如仍不存在,则不启用缓存.
  54. * @var string
  55. */
  56. private $memcacheClusterFlag;
  57. /**
  58. *
  59. * 插入行为
  60. * @var string
  61. */
  62. const PARAM_CREATE_ACTION_INSERT = 'INSERT INTO';
  63. /**
  64. *
  65. * 插入,但重复时忽略
  66. * @var string
  67. */
  68. const PARAM_CREATE_ACTION_INSERT_IGNORE = 'INSERT IGNORE';
  69. /**
  70. *
  71. * 插入,但重复时完全replace
  72. * @var string
  73. */
  74. const PARAM_CREATE_ACTION_REPLACE = 'REPLACE INTO';
  75. /**
  76. *
  77. * 插入,但重复时自动转为update
  78. * @var string
  79. */
  80. const PARAM_CREATE_ACTION_ONDUPLICATE = 'ON DUPLICATE KEY UPDATE';
  81. /**
  82. * 数据库实例,可被动态切换到主库或从库
  83. *
  84. * @var KIF\Db\MySQLi
  85. */
  86. protected $db;
  87. /**
  88. *
  89. * mysql集群标志
  90. * @var string
  91. */
  92. private $cluster_flag;
  93. /**
  94. *
  95. * @param string $cluster_flag mysql集群标志
  96. *
  97. */
  98. public function __construct($cluster_flag = 'default') {
  99. $appConfig = Config::getInstance()->current();
  100. $dbConfig = $appConfig['db'];
  101. if (!$dbConfig || !isset($dbConfig[$cluster_flag]) || !is_string($dbConfig[$cluster_flag])) {
  102. throw new ParamsException("load config error:{$dbConfig}");
  103. }
  104. $this->cluster_flag = $cluster_flag;
  105. if (isset($appConfig['memcached']['dao_cache'])) {
  106. $this->memcacheClusterFlag = 'dao_cache';
  107. } elseif (isset($appConfig['memcached']['default'])) {
  108. $this->memcacheClusterFlag = 'default';
  109. } else {# 没有缓存配置,不启用缓存
  110. $this->useCache(false);
  111. }
  112. $dsn = $dbConfig[$cluster_flag];
  113. $this->db = new MySQLi($dsn);
  114. }
  115. /**
  116. *
  117. * 获取设置的主键名
  118. * @return string
  119. */
  120. public function getPrimaryKey() {
  121. return $this->primaryKey;
  122. }
  123. /**
  124. * 设置表名
  125. * 动态设置表名是为了拆表
  126. * @param $tableName
  127. */
  128. public function setTableName($tableName) {
  129. $this->tableName = $tableName;
  130. }
  131. /**
  132. *
  133. * 控制是否使用缓存。使用缓存,会将数据表的行记录缓存起来,极大的提高效率和高可用;同时也会在记录删除、更新时,清空对应的缓存。
  134. * @param boolean $useCache
  135. */
  136. public function useCache($useCache = true) {
  137. $this->useCache = (bool) $useCache;
  138. }
  139. /**
  140. * 解析条件
  141. * @param array|string $condition
  142. * @throws Exception parameter errors
  143. * @return string
  144. */
  145. public function parseCondition($condition = null) {
  146. if(empty($condition)) return '';
  147. // 如果条件语句是字符串 直接拼接
  148. if(is_string($condition)){
  149. return "
  150. WHERE ". $condition ." ";
  151. }
  152. // 如果不是字符串 强制传递数组 如果不是数组抛异常
  153. if(!is_array($condition)){
  154. throw new Exception('parameter errors');
  155. }
  156. $condition = $this->quote($condition);
  157. $where = array();
  158. foreach($condition as $tmpK => $tmpV) {
  159. # 避免条件字段重复,支持二维数组。比如$conditon = array(
  160. # array(
  161. # 'publish_time' => SqlHelper::addCompareOperator(">=", $s_ts),
  162. # ),
  163. # array(
  164. # 'publish_time' => SqlHelper::addCompareOperator("<", $e_ts),
  165. # ),
  166. # );
  167. # 以前只能这样变相支持:$condition = array(
  168. # 'publish_time' => SqlHelper::addCompareOperator(">=", $s_ts),
  169. # '`publish_time`' => SqlHelper::addCompareOperator("<", $e_ts),
  170. # );
  171. if (Verify::naturalNumber($tmpK)) {
  172. list($key, $val) = each($tmpV);
  173. } else {
  174. $key = $tmpK;
  175. $val = $tmpV;
  176. }
  177. if (strpos($key, '`') === false // 没用使用 ` 字符,比如 `status`
  178. && strpos($key, '(') === false) // 也不含有括号(即不是函数),比如 last_insert_id(status)
  179. {
  180. $key = "`{$key}`";
  181. }
  182. if(is_scalar($val)) {
  183. $where[] = "{$key} " . SqlHelper::explodeCompareOperator($val);
  184. } elseif(is_array($val)) {
  185. $where[] = "{$key} IN (".join(',', $val).")";
  186. } else {
  187. throw new Exception('parameter errors');
  188. }
  189. }
  190. return "
  191. WHERE ".join(' && ', $where)."
  192. ";
  193. }
  194. /**
  195. * 根据条件查找主键id列表集
  196. * 做这个方法,是出于架构上的考虑。只取出主键id集,然后通过主键id集才去取数据(内存或数据库)
  197. * @param array|String $condition 条件
  198. * @param string | int $limit 指定分页
  199. * @param string $order 指定排序
  200. * @return array 如:array(123, 124)。无值时返回空数组
  201. */
  202. public function findIdsBy($condition = null, $limit = null, $order = null) {
  203. $result = $this->findBy($condition, $this->primaryKey, $limit, $this->primaryKey, $order);
  204. if (! $result) {
  205. return array();
  206. }
  207. return array_keys($result);
  208. }
  209. /**
  210. * 查找的底层方法。对查找只提供有限支持,太复杂的查询请手动sql
  211. * final 让该方法禁止被继承
  212. * 1、根据条件查找,多条件只支持 与(&&),不支持或之类的
  213. * 2、支持limit
  214. * 3、group by
  215. * @param array|string $condition
  216. * @param string $returnAssociateKey 如果指定该指,返回的值不再以0,1,2为key,而是以其对应的值为key
  217. * @param string | int $limit
  218. * @param string $selectCols 要获取的列。语法:id, uid ,默认为 *
  219. * @param string $order 指定排序
  220. * @return array
  221. */
  222. final public function findBy($condition = null, $returnAssociateKey = null, $limit = null, $selectCols = '*', $order = null) {
  223. $where = $this->parseCondition($condition);
  224. if (!isset($limit) || !preg_match('#^(?:\d+\s*,\s*)?\d+$#', $limit)) {
  225. $strLimit = ' ';
  226. } else {
  227. $strLimit = " LIMIT $limit ";
  228. }
  229. $strOrder = '';
  230. if(!empty($order)) {
  231. $strOrder = " ORDER BY {$order} ";
  232. }
  233. if(!isset($selectCols)) {
  234. $selectCols = '*';
  235. }
  236. $sql = "SELECT {$selectCols} FROM {$this->tableName}
  237. {$where}
  238. {$strOrder}
  239. {$strLimit}
  240. ;
  241. ";
  242. return $this->db->fetchAll($sql, $returnAssociateKey);
  243. }
  244. public function fetchOne($condition = null, $selectCols = '*', $order = null) {
  245. $result = self::findBy($condition, null, 1, $selectCols, $order);
  246. if (!$result) {
  247. return false;
  248. }
  249. return array_pop($result);
  250. }
  251. /**
  252. * 获取所有
  253. * @param string $order 排序方式。默认以主键倒序;有效的格式:"create_time desc"
  254. * @param string $limit 该参数用于支持分页,默认为不使用分页。格式 "offset, length"
  255. * @return array
  256. */
  257. public function getsAll($order = null, $limit = null) {
  258. $ids = self::getsAllIds($order, $limit);
  259. return $this->gets($ids);
  260. }
  261. /**
  262. * 获取所有id列表集
  263. * @param string $order 排序方式。默认以主键倒序;有效的格式:"create_time desc"
  264. * @param string $limit 该参数用于支持分页,默认为不使用分页。格式 "offset, length"
  265. * @return array
  266. */
  267. public function getsAllIds($order = null, $limit = null) {
  268. if (is_null($order)) {
  269. $order = "{$this->primaryKey} desc";
  270. }
  271. if (!is_string($order)) {
  272. throw new Exception('$order 必须是字符串或null');
  273. }
  274. $condition = null;
  275. $ids = self::findIdsBy($condition, $limit, $order);
  276. return $ids;
  277. }
  278. /**
  279. * 获取指定$id
  280. * @param int | string $id
  281. * @return false | array
  282. */
  283. public function get($id) {
  284. if (Verify::int($id)) {
  285. if ($id < 1) {
  286. return false;
  287. }
  288. } elseif (is_string($id)) {
  289. if (strlen($id) == 0) {
  290. return false;
  291. }
  292. } else {
  293. return false;
  294. }
  295. $result = self::gets(array($id));// 调用该类自身的gets方法,而不是继承者的gets方法!
  296. if (!$result) {
  297. return false;
  298. }
  299. return array_pop($result);
  300. }
  301. /**
  302. * 批量获取信息
  303. * @param array $ids id组成的数组
  304. * @return array 无结果时返回空数组
  305. */
  306. public function gets(array $ids) {
  307. $return = array();
  308. if (empty($ids)) {
  309. return $return;
  310. }
  311. $ids = array_unique($ids);
  312. # 从缓存取
  313. $cache_return = self::getsFromCache($ids);
  314. $non_cache_ids = array_diff($ids, array_keys($cache_return));
  315. if (!$non_cache_ids) {
  316. return $cache_return;
  317. }
  318. # 从DB取
  319. $db_return = self::getsFromDB($non_cache_ids);
  320. if ($db_return) {
  321. self::setToCache($db_return);
  322. }
  323. foreach ($ids as $id) {
  324. if (isset($cache_return[$id])) {
  325. $return[$id] = $cache_return[$id];
  326. continue;
  327. }
  328. if (isset($db_return[$id])) {
  329. $return[$id] = $db_return[$id];
  330. continue;
  331. }
  332. }
  333. return $return;
  334. }
  335. /**
  336. *
  337. * 从后端数据库取结果
  338. * @param array $ids
  339. * @return array 格式:array(
  340. * (int) id => (mixed) value,
  341. * ... ,
  342. * )
  343. *
  344. */
  345. private function getsFromDB(array $ids) {
  346. $return = array();
  347. $condition = array(
  348. $this->primaryKey => $ids,
  349. );
  350. $result = $this->findBy($condition, $this->primaryKey);
  351. if (!$result) {
  352. return $return;
  353. }
  354. foreach ($ids as $id) {
  355. $id = (string) $id;// php的数组下标是int整时,如果超出了操作系统平台的最大有符号正整数后,会取不到值。转成字符串型,解决这个bug
  356. if (array_key_exists($id, $result)) {
  357. $return[$id] = $result[$id];
  358. }
  359. }
  360. return $return;
  361. }
  362. /**
  363. *
  364. * 获取表缓存的标志。后续这个表里每行的缓存,都会关联上这个表缓存标志。
  365. * 所以只要清空这个标志,即可使整个表记录的缓存失效。
  366. * @Exception DaoException::CACHE_SET_TABLE_FLAG_ERROR
  367. * @Exception DaoException::CACHE_SERVICE_UNAVAILABLE
  368. * @return String
  369. */
  370. public function getTableCacheFlag() {
  371. if ($this->tableCacheFlag) {
  372. return $this->tableCacheFlag;
  373. }
  374. $dbconfig = Config::getInstance()->get('db');
  375. $cacheKey_elements = array(
  376. serialize($dbconfig[$this->cluster_flag]),
  377. $this->tableName,
  378. 'table_cache_flag',
  379. );
  380. $key = Math::md5_16(join($cacheKey_elements, '::'));
  381. $objMemcached = new Memcached($this->memcacheClusterFlag);
  382. $tmpGetResult = $objMemcached->get($key);
  383. if ($tmpGetResult) {
  384. $this->tableCacheFlag = $tmpGetResult;
  385. return $this->tableCacheFlag;
  386. }
  387. if (is_null($tmpGetResult)) {// 缓存不存在,建立
  388. $tmpVal = microtime(true);
  389. if ($objMemcached->set($key, $tmpVal, 0)) {// 永不过期
  390. $this->tableCacheFlag = $tmpVal;
  391. return $this->tableCacheFlag;
  392. }
  393. # TODO 设置缓存失败的处理
  394. throw new DaoException(DaoException::CACHE_SET_TABLE_FLAG_ERROR);
  395. }
  396. if (!$tmpGetResult) {
  397. # TODO memcache服务不可用的处理
  398. throw new DaoException(DaoException::CACHE_SERVICE_UNAVAILABLE);
  399. }
  400. }
  401. /**
  402. *
  403. * 获取指定ids集对应的缓存key集
  404. * @param array $ids array(13343, 9939);
  405. * @Exception DaoException::CACHE_SET_TABLE_FLAG_ERROR
  406. * @Exception DaoException::CACHE_SERVICE_UNAVAILABLE
  407. * @return array 格式:array(
  408. * (int) id => (string) cacheKey,
  409. * ... ,
  410. *
  411. * )
  412. */
  413. public function getsCacheKeys(array $ids) {
  414. $tableCacheFlag = $this->getTableCacheFlag();
  415. $cacheKeys = array();
  416. $dbconfig = Config::getInstance()->get('db');
  417. foreach ($ids as $id) {
  418. $cacheKey_elements = array(
  419. $tableCacheFlag,
  420. serialize($dbconfig[$this->cluster_flag]),
  421. $this->tableName,
  422. $id,
  423. 'row_cache',// 这是针对数据库记录行的缓存
  424. );
  425. $cacheKeys[$id] = Math::md5_16(join($cacheKey_elements, '::'));
  426. }
  427. return $cacheKeys;
  428. }
  429. /**
  430. *
  431. * 从缓存里取数据表对应的行记录
  432. * @param array $ids
  433. * @return array 只返回缓存里有的值,如果缓存里没有任何值,则返回空数组
  434. */
  435. private function getsFromCache(array $ids) {
  436. $return = array();
  437. if (!$this->useCache) {
  438. return $return;
  439. }
  440. try {
  441. $cacheKeys = self::getsCacheKeys($ids);
  442. } catch (DaoException $e) {
  443. return $return;
  444. }
  445. $objMemcached = new Memcached($this->memcacheClusterFlag);
  446. $cacheVals = $objMemcached->gets($cacheKeys);
  447. $cache_return = array_combine(array_keys($cacheKeys), $cacheVals);
  448. foreach ($cacheVals as $tmpK => $tmpV) {
  449. if (is_null($tmpV)) {
  450. continue;
  451. }
  452. $return[array_search($tmpK, $cacheKeys)] = $tmpV;
  453. }
  454. return $return;
  455. }
  456. /**
  457. *
  458. * 从缓存里将$id对应的行记录删除
  459. * @param int $id
  460. * @throws Exception
  461. * @return Boolean
  462. */
  463. private function deleteFromCache($id) {
  464. if (!$this->useCache) {
  465. return false;
  466. }
  467. $objMemcached = new Memcached($this->memcacheClusterFlag);
  468. if (Verify::unsignedInt($id)) {
  469. try {
  470. $cacheKeys = self::getsCacheKeys(array($id));
  471. } catch (DaoException $e) {
  472. return false;
  473. }
  474. return $objMemcached->delete(array_pop($cacheKeys));
  475. }
  476. if (is_array($id) && !empty($id)) {
  477. try {
  478. $cacheKeys = self::getsCacheKeys($id);
  479. } catch (DaoException $e) {
  480. return false;
  481. }
  482. if (count($cacheKeys) == 1) {
  483. return $objMemcached->delete(array_pop($cacheKeys));
  484. }
  485. return $objMemcached->deletes($cacheKeys);
  486. }
  487. return false;
  488. }
  489. /**
  490. *
  491. * 清除缓存的这张表的所有记录
  492. * @return Boolean
  493. */
  494. public function deleteAllCaches() {
  495. if (!$this->useCache) {
  496. return false;
  497. }
  498. try {
  499. $tableCacheFlag = $this->getTableCacheFlag();
  500. } catch (DaoException $e) {
  501. return false;
  502. }
  503. $objMemcached = new Memcached($this->memcacheClusterFlag);
  504. return $objMemcached->delete($tableCacheFlag);
  505. }
  506. /**
  507. *
  508. * 将gets出来的结果集,存到缓存
  509. * @param array $results
  510. * @return boolean
  511. */
  512. private function setToCache(array $results) {
  513. if (!$this->useCache) {
  514. return false;
  515. }
  516. if (!$results) {
  517. return false;
  518. }
  519. try {
  520. $cacheKeys = self::getsCacheKeys(array_keys($results));
  521. } catch (DaoException $e) {
  522. return false;
  523. }
  524. $objMemcached = new Memcached($this->memcacheClusterFlag);
  525. return $objMemcached->sets(array_combine($cacheKeys, $results), $this->cache_expiration);
  526. }
  527. /**
  528. * 创建一条记录
  529. * @param array $tableInfo 待插入的数据
  530. * @param boolean $isAutoIncrement 操作成功时,如果该值为true,返回最后插入的id;否则返回true
  531. * @return boolean | int
  532. */
  533. private function _create(array $tableInfo, $isAutoIncrement = true, $action = self::PARAM_CREATE_ACTION_INSERT) {
  534. if(empty($tableInfo)) return false;
  535. switch($action) {
  536. case self::PARAM_CREATE_ACTION_INSERT :
  537. case self::PARAM_CREATE_ACTION_INSERT_IGNORE :
  538. case self::PARAM_CREATE_ACTION_REPLACE :
  539. break;
  540. default:
  541. throw new Exception('error insert action');
  542. }
  543. $sql = "{$action} {$this->tableName}
  544. SET
  545. ";
  546. $sqlSets = '';
  547. $tableInfo = $this->quote($tableInfo);
  548. foreach($tableInfo as $key => $val) {
  549. if($sqlSets != '') $sqlSets .= ' ,';
  550. $sqlSets .= "
  551. `{$key}` = {$val}
  552. ";
  553. }
  554. $sql .= $sqlSets;
  555. if($this->db->query($sql)) {
  556. if($isAutoIncrement) {
  557. $id = $this->db->insertId();
  558. # 清空这条记录的缓存
  559. self::deleteFromCache($id);
  560. return $id > 0 ? $id : true;
  561. } else {
  562. return true;
  563. }
  564. }
  565. return false;
  566. }
  567. /**
  568. * 创建一条记录,如果重复,则替换
  569. * @param array $tableInfo 待插入的数据
  570. * @param boolean $isAutoIncrement 操作成功时,如果该值为true,返回最后插入的id;否则返回true
  571. * @return boolean | int
  572. */
  573. public function replace(array $tableInfo, $isAutoIncrement = true) {
  574. return $this->_create($tableInfo, $isAutoIncrement, self::PARAM_CREATE_ACTION_REPLACE);
  575. }
  576. /**
  577. * 创建一条记录
  578. * @param array $tableInfo 待插入的数据
  579. * @param boolean $isAutoIncrement 操作成功时,如果该值为true,返回最后插入的id;否则返回true
  580. * @return boolean | int
  581. */
  582. public function create(array $tableInfo, $isAutoIncrement = true) {
  583. return $this->_create($tableInfo, $isAutoIncrement, self::PARAM_CREATE_ACTION_INSERT);
  584. }
  585. /**
  586. * 创建一条记录,如果重复,则忽略
  587. * @param array $tableInfo 待插入的数据
  588. * @param boolean $isAutoIncrement 操作成功时,如果该值为true,返回最后插入的id;否则返回true
  589. * @return boolean | int PS:$isAutoIncrement = true时:1、如果插入了,返回自动id值;2、如果已存在,返回true。
  590. */
  591. public function insertIgnore(array $tableInfo, $isAutoIncrement = true) {
  592. return $this->_create($tableInfo, $isAutoIncrement, self::PARAM_CREATE_ACTION_INSERT_IGNORE);
  593. }
  594. /**
  595. *
  596. * 插入一条记录,如果重复,自动转为更新语句
  597. * @param array $tableInfo
  598. * @param array $onDuplicate 如果重复时,需要更新的信息。如果不指定,则使用$tableInfo的值,即认为要全部更新
  599. * @return int | Boolean
  600. * int:只要存在,无论之前记录是否存在,都会返回记录的id;
  601. * true:执行成功,但获取记录id时失败;
  602. * false:执行失败
  603. */
  604. public function insertDuplicate(array $tableInfo, array $onDuplicate = array()) {
  605. if (!$tableInfo) {
  606. return false;
  607. }
  608. $tmpArrKeys = array();
  609. foreach ($tableInfo as $tmpKey => $tmpV) {
  610. $tmpArrKeys[] = "`{$tmpKey}`";
  611. }
  612. $sql = "INSERT INTO {$this->tableName} (" . join(', ', $tmpArrKeys). ") VALUES ";
  613. $tmpArrValues = array();
  614. $new_tableInfo = $this->quote($tableInfo);
  615. foreach ($new_tableInfo as $tmpKey => $tmpV) {
  616. $tmpArrValues[] = $tmpV;
  617. }
  618. $sql .= " ( " . join(', ', $tmpArrValues) . " ) ";
  619. $sql .= "
  620. ON DUPLICATE KEY UPDATE
  621. ";
  622. $tmpArrDps = array();
  623. if (empty($onDuplicate)) {
  624. $onDuplicate = $tableInfo;
  625. }
  626. $new_onDuplicate = $this->quote($onDuplicate);
  627. foreach ($new_onDuplicate as $tmpKey => $tmpV) {
  628. $tmpArrDps[] = " `{$tmpKey}` = {$tmpV} ";
  629. }
  630. $sql .= join(', ', $tmpArrDps);
  631. if (!$this->db->query($sql)) {
  632. return false;
  633. }
  634. $id = $this->db->insertId();
  635. # 清空这条记录的缓存
  636. self::deleteFromCache($id);
  637. return $id > 0 ? $id : true;
  638. }
  639. /**
  640. * 根据条件更新指定数据
  641. * @param array $tableInfo 待更新的数据(与数据库字段对应的数据)
  642. * @param array $condition 条件(与数据库字段对应的数据)
  643. * @return boolean
  644. */
  645. public function update(array $tableInfo, array $condition) {
  646. if(empty($tableInfo)) return false;
  647. $sql = "UPDATE {$this->tableName}
  648. SET
  649. ";
  650. $sqlSets = '';
  651. foreach($tableInfo as $key => $val) {
  652. if($sqlSets != '') $sqlSets .= ' ,';
  653. $sqlSets .= "
  654. `{$key}` = {$this->quote($val)}
  655. ";
  656. }
  657. $sql .= $sqlSets;
  658. $where = $this->parseCondition($condition);
  659. $sql .= "
  660. {$where}
  661. ;
  662. ";
  663. $tmpQueryResult = $this->db->query($sql);
  664. if ($tmpQueryResult) {
  665. if (isset($condition[$this->primaryKey])) {
  666. # 清空这条记录的缓存,条件里有可能是一批ids
  667. self::deleteFromCache($condition[$this->primaryKey]);
  668. } else {
  669. $this->deleteAllCaches();
  670. }
  671. }
  672. return $tmpQueryResult;
  673. }
  674. /**
  675. * 根据条件删除数据
  676. * @param Array|String $condition 条件
  677. * @return boolean
  678. */
  679. public function delete($condition) {
  680. $where = $this->parseCondition($condition);
  681. $sql = "DELETE FROM {$this->tableName}
  682. {$where}
  683. ;
  684. ";
  685. $tmpQueryResult = $this->db->query($sql);
  686. if ($tmpQueryResult) {
  687. if (isset($condition[$this->primaryKey])) {
  688. # 清空这条记录的缓存,条件里有可能是一批ids
  689. self::deleteFromCache($condition[$this->primaryKey]);
  690. } else {
  691. $this->deleteAllCaches();
  692. }
  693. }
  694. return $tmpQueryResult;
  695. }
  696. /**
  697. * 转义数据
  698. * @param mixed $data
  699. */
  700. public function quote($data) {
  701. return SqlHelper::escape($data, true);
  702. }
  703. /**
  704. * 判断给定的值是否是有效的自增主键值
  705. * @param mixid $pk
  706. * @return boolean
  707. */
  708. static protected function isValidPK($pk) {
  709. return SqlHelper::isValidPK($pk);
  710. }
  711. /**
  712. * 判断给定数组的某个key的值,是否是有效的自增主键值
  713. * @param array $arr
  714. * @param mixid $key
  715. * @return boolean
  716. */
  717. static protected function isValidPKWithArray(array $arr, $key) {
  718. return SqlHelper::isValidPKWithArray($arr, $key);
  719. }
  720. /**
  721. * 获取指定条件的记录数
  722. * @return int
  723. */
  724. public function totals($condtion = null){
  725. $result = $this->findBy($condtion, null, null, "count(*)");
  726. if (!$result) {
  727. return 0;
  728. }
  729. return (int) array_pop(array_pop($result));
  730. }
  731. }