AbstractDao.class.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  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. public function fetchAll($condition = null, $limit = 10, $order = null, $selectCols = '*') {
  252. $result = self::findBy($condition, null, $limit, $selectCols, $order);
  253. if (!$result) {
  254. return false;
  255. }
  256. foreach ($result as & $tmpV) {
  257. $tmpExtend = array();
  258. $tmpExtend = unserialize($tmpV['extend']);
  259. unset($tmpV['extend']);
  260. # 将存在扩展字段里的信息提出来
  261. if (is_array($tmpExtend)) foreach ($tmpExtend as $tmpKK => $tmpVV) {
  262. if (!in_array($tmpKK, $this->real_field)) {
  263. $tmpV[$tmpKK] = $tmpVV;
  264. }
  265. }
  266. }
  267. return $result;
  268. }
  269. /**
  270. * 获取所有
  271. * @param string $order 排序方式。默认以主键倒序;有效的格式:"create_time desc"
  272. * @param string $limit 该参数用于支持分页,默认为不使用分页。格式 "offset, length"
  273. * @return array
  274. */
  275. public function getsAll($order = null, $limit = null) {
  276. $ids = self::getsAllIds($order, $limit);
  277. return $this->gets($ids);
  278. }
  279. /**
  280. * 获取所有id列表集
  281. * @param string $order 排序方式。默认以主键倒序;有效的格式:"create_time desc"
  282. * @param string $limit 该参数用于支持分页,默认为不使用分页。格式 "offset, length"
  283. * @return array
  284. */
  285. public function getsAllIds($order = null, $limit = null) {
  286. if (is_null($order)) {
  287. $order = "{$this->primaryKey} desc";
  288. }
  289. if (!is_string($order)) {
  290. throw new Exception('$order 必须是字符串或null');
  291. }
  292. $condition = null;
  293. $ids = self::findIdsBy($condition, $limit, $order);
  294. return $ids;
  295. }
  296. /**
  297. * 获取指定$id
  298. * @param int | string $id
  299. * @return false | array
  300. */
  301. public function get($id) {
  302. if (Verify::int($id)) {
  303. if ($id < 1) {
  304. return false;
  305. }
  306. } elseif (is_string($id)) {
  307. if (strlen($id) == 0) {
  308. return false;
  309. }
  310. } else {
  311. return false;
  312. }
  313. $result = self::gets(array($id));// 调用该类自身的gets方法,而不是继承者的gets方法!
  314. if (!$result) {
  315. return false;
  316. }
  317. return array_pop($result);
  318. }
  319. /**
  320. * 批量获取信息
  321. * @param array $ids id组成的数组
  322. * @return array 无结果时返回空数组
  323. */
  324. public function gets(array $ids) {
  325. $return = array();
  326. if (empty($ids)) {
  327. return $return;
  328. }
  329. $ids = array_unique($ids);
  330. # 从缓存取
  331. $cache_return = self::getsFromCache($ids);
  332. $non_cache_ids = array_diff($ids, array_keys($cache_return));
  333. if (!$non_cache_ids) {
  334. return $cache_return;
  335. }
  336. # 从DB取
  337. $db_return = self::getsFromDB($non_cache_ids);
  338. if ($db_return) {
  339. self::setToCache($db_return);
  340. }
  341. foreach ($ids as $id) {
  342. if (isset($cache_return[$id])) {
  343. $return[$id] = $cache_return[$id];
  344. continue;
  345. }
  346. if (isset($db_return[$id])) {
  347. $return[$id] = $db_return[$id];
  348. continue;
  349. }
  350. }
  351. return $return;
  352. }
  353. /**
  354. *
  355. * 从后端数据库取结果
  356. * @param array $ids
  357. * @return array 格式:array(
  358. * (int) id => (mixed) value,
  359. * ... ,
  360. * )
  361. *
  362. */
  363. private function getsFromDB(array $ids) {
  364. $return = array();
  365. $condition = array(
  366. $this->primaryKey => $ids,
  367. );
  368. $result = $this->findBy($condition, $this->primaryKey);
  369. if (!$result) {
  370. return $return;
  371. }
  372. foreach ($ids as $id) {
  373. $id = (string) $id;// php的数组下标是int整时,如果超出了操作系统平台的最大有符号正整数后,会取不到值。转成字符串型,解决这个bug
  374. if (array_key_exists($id, $result)) {
  375. $return[$id] = $result[$id];
  376. }
  377. }
  378. return $return;
  379. }
  380. /**
  381. *
  382. * 获取表缓存的标志。后续这个表里每行的缓存,都会关联上这个表缓存标志。
  383. * 所以只要清空这个标志,即可使整个表记录的缓存失效。
  384. * @Exception DaoException::CACHE_SET_TABLE_FLAG_ERROR
  385. * @Exception DaoException::CACHE_SERVICE_UNAVAILABLE
  386. * @return String
  387. */
  388. public function getTableCacheFlag() {
  389. if ($this->tableCacheFlag) {
  390. return $this->tableCacheFlag;
  391. }
  392. $dbconfig = Config::getInstance()->get('db');
  393. $cacheKey_elements = array(
  394. serialize($dbconfig[$this->cluster_flag]),
  395. $this->tableName,
  396. 'table_cache_flag',
  397. );
  398. $key = Math::md5_16(join($cacheKey_elements, '::'));
  399. $objMemcached = new Memcached($this->memcacheClusterFlag);
  400. $tmpGetResult = $objMemcached->get($key);
  401. if ($tmpGetResult) {
  402. $this->tableCacheFlag = $tmpGetResult;
  403. return $this->tableCacheFlag;
  404. }
  405. if (is_null($tmpGetResult)) {// 缓存不存在,建立
  406. $tmpVal = microtime(true);
  407. if ($objMemcached->set($key, $tmpVal, 0)) {// 永不过期
  408. $this->tableCacheFlag = $tmpVal;
  409. return $this->tableCacheFlag;
  410. }
  411. # TODO 设置缓存失败的处理
  412. throw new DaoException(DaoException::CACHE_SET_TABLE_FLAG_ERROR);
  413. }
  414. if (!$tmpGetResult) {
  415. # TODO memcache服务不可用的处理
  416. throw new DaoException(DaoException::CACHE_SERVICE_UNAVAILABLE);
  417. }
  418. }
  419. /**
  420. *
  421. * 获取指定ids集对应的缓存key集
  422. * @param array $ids array(13343, 9939);
  423. * @Exception DaoException::CACHE_SET_TABLE_FLAG_ERROR
  424. * @Exception DaoException::CACHE_SERVICE_UNAVAILABLE
  425. * @return array 格式:array(
  426. * (int) id => (string) cacheKey,
  427. * ... ,
  428. *
  429. * )
  430. */
  431. public function getsCacheKeys(array $ids) {
  432. $tableCacheFlag = $this->getTableCacheFlag();
  433. $cacheKeys = array();
  434. $dbconfig = Config::getInstance()->get('db');
  435. foreach ($ids as $id) {
  436. $cacheKey_elements = array(
  437. $tableCacheFlag,
  438. serialize($dbconfig[$this->cluster_flag]),
  439. $this->tableName,
  440. $id,
  441. 'row_cache',// 这是针对数据库记录行的缓存
  442. );
  443. $cacheKeys[$id] = Math::md5_16(join($cacheKey_elements, '::'));
  444. }
  445. return $cacheKeys;
  446. }
  447. /**
  448. *
  449. * 从缓存里取数据表对应的行记录
  450. * @param array $ids
  451. * @return array 只返回缓存里有的值,如果缓存里没有任何值,则返回空数组
  452. */
  453. private function getsFromCache(array $ids) {
  454. $return = array();
  455. if (!$this->useCache) {
  456. return $return;
  457. }
  458. try {
  459. $cacheKeys = self::getsCacheKeys($ids);
  460. } catch (DaoException $e) {
  461. return $return;
  462. }
  463. $objMemcached = new Memcached($this->memcacheClusterFlag);
  464. $cacheVals = $objMemcached->gets($cacheKeys);
  465. $cache_return = array_combine(array_keys($cacheKeys), $cacheVals);
  466. foreach ($cacheVals as $tmpK => $tmpV) {
  467. if (is_null($tmpV)) {
  468. continue;
  469. }
  470. $return[array_search($tmpK, $cacheKeys)] = $tmpV;
  471. }
  472. return $return;
  473. }
  474. /**
  475. *
  476. * 从缓存里将$id对应的行记录删除
  477. * @param int $id
  478. * @throws Exception
  479. * @return Boolean
  480. */
  481. private function deleteFromCache($id) {
  482. if (!$this->useCache) {
  483. return false;
  484. }
  485. $objMemcached = new Memcached($this->memcacheClusterFlag);
  486. if (Verify::unsignedInt($id)) {
  487. try {
  488. $cacheKeys = self::getsCacheKeys(array($id));
  489. } catch (DaoException $e) {
  490. return false;
  491. }
  492. return $objMemcached->delete(array_pop($cacheKeys));
  493. }
  494. if (is_array($id) && !empty($id)) {
  495. try {
  496. $cacheKeys = self::getsCacheKeys($id);
  497. } catch (DaoException $e) {
  498. return false;
  499. }
  500. if (count($cacheKeys) == 1) {
  501. return $objMemcached->delete(array_pop($cacheKeys));
  502. }
  503. return $objMemcached->deletes($cacheKeys);
  504. }
  505. return false;
  506. }
  507. /**
  508. *
  509. * 清除缓存的这张表的所有记录
  510. * @return Boolean
  511. */
  512. public function deleteAllCaches() {
  513. if (!$this->useCache) {
  514. return false;
  515. }
  516. try {
  517. $tableCacheFlag = $this->getTableCacheFlag();
  518. } catch (DaoException $e) {
  519. return false;
  520. }
  521. $objMemcached = new Memcached($this->memcacheClusterFlag);
  522. return $objMemcached->delete($tableCacheFlag);
  523. }
  524. /**
  525. *
  526. * 将gets出来的结果集,存到缓存
  527. * @param array $results
  528. * @return boolean
  529. */
  530. private function setToCache(array $results) {
  531. if (!$this->useCache) {
  532. return false;
  533. }
  534. if (!$results) {
  535. return false;
  536. }
  537. try {
  538. $cacheKeys = self::getsCacheKeys(array_keys($results));
  539. } catch (DaoException $e) {
  540. return false;
  541. }
  542. $objMemcached = new Memcached($this->memcacheClusterFlag);
  543. return $objMemcached->sets(array_combine($cacheKeys, $results), $this->cache_expiration);
  544. }
  545. /**
  546. * 创建一条记录
  547. * @param array $tableInfo 待插入的数据
  548. * @param boolean $isAutoIncrement 操作成功时,如果该值为true,返回最后插入的id;否则返回true
  549. * @return boolean | int
  550. */
  551. private function _create(array $tableInfo, $isAutoIncrement = true, $action = self::PARAM_CREATE_ACTION_INSERT) {
  552. if(empty($tableInfo)) return false;
  553. switch($action) {
  554. case self::PARAM_CREATE_ACTION_INSERT :
  555. case self::PARAM_CREATE_ACTION_INSERT_IGNORE :
  556. case self::PARAM_CREATE_ACTION_REPLACE :
  557. break;
  558. default:
  559. throw new Exception('error insert action');
  560. }
  561. $sql = "{$action} {$this->tableName}
  562. SET
  563. ";
  564. $sqlSets = '';
  565. $tableInfo = $this->quote($tableInfo);
  566. foreach($tableInfo as $key => $val) {
  567. if($sqlSets != '') $sqlSets .= ' ,';
  568. $sqlSets .= "
  569. `{$key}` = {$val}
  570. ";
  571. }
  572. $sql .= $sqlSets;
  573. if($this->db->query($sql)) {
  574. if($isAutoIncrement) {
  575. $id = $this->db->insertId();
  576. # 清空这条记录的缓存
  577. self::deleteFromCache($id);
  578. return $id > 0 ? $id : true;
  579. } else {
  580. return true;
  581. }
  582. }
  583. return false;
  584. }
  585. /**
  586. * 创建一条记录,如果重复,则替换
  587. * @param array $tableInfo 待插入的数据
  588. * @param boolean $isAutoIncrement 操作成功时,如果该值为true,返回最后插入的id;否则返回true
  589. * @return boolean | int
  590. */
  591. public function replace(array $tableInfo, $isAutoIncrement = true) {
  592. return $this->_create($tableInfo, $isAutoIncrement, self::PARAM_CREATE_ACTION_REPLACE);
  593. }
  594. /**
  595. * 创建一条记录
  596. * @param array $tableInfo 待插入的数据
  597. * @param boolean $isAutoIncrement 操作成功时,如果该值为true,返回最后插入的id;否则返回true
  598. * @return boolean | int
  599. */
  600. public function create(array $tableInfo, $isAutoIncrement = true) {
  601. return $this->_create($tableInfo, $isAutoIncrement, self::PARAM_CREATE_ACTION_INSERT);
  602. }
  603. /**
  604. * 创建一条记录,如果重复,则忽略
  605. * @param array $tableInfo 待插入的数据
  606. * @param boolean $isAutoIncrement 操作成功时,如果该值为true,返回最后插入的id;否则返回true
  607. * @return boolean | int PS:$isAutoIncrement = true时:1、如果插入了,返回自动id值;2、如果已存在,返回true。
  608. */
  609. public function insertIgnore(array $tableInfo, $isAutoIncrement = true) {
  610. return $this->_create($tableInfo, $isAutoIncrement, self::PARAM_CREATE_ACTION_INSERT_IGNORE);
  611. }
  612. /**
  613. *
  614. * 插入一条记录,如果重复,自动转为更新语句
  615. * @param array $tableInfo
  616. * @param array $onDuplicate 如果重复时,需要更新的信息。如果不指定,则使用$tableInfo的值,即认为要全部更新
  617. * @return int | Boolean
  618. * int:只要存在,无论之前记录是否存在,都会返回记录的id;
  619. * true:执行成功,但获取记录id时失败;
  620. * false:执行失败
  621. */
  622. public function insertDuplicate(array $tableInfo, array $onDuplicate = array()) {
  623. if (!$tableInfo) {
  624. return false;
  625. }
  626. $tmpArrKeys = array();
  627. foreach ($tableInfo as $tmpKey => $tmpV) {
  628. $tmpArrKeys[] = "`{$tmpKey}`";
  629. }
  630. $sql = "INSERT INTO {$this->tableName} (" . join(', ', $tmpArrKeys). ") VALUES ";
  631. $tmpArrValues = array();
  632. $new_tableInfo = $this->quote($tableInfo);
  633. foreach ($new_tableInfo as $tmpKey => $tmpV) {
  634. $tmpArrValues[] = $tmpV;
  635. }
  636. $sql .= " ( " . join(', ', $tmpArrValues) . " ) ";
  637. $sql .= "
  638. ON DUPLICATE KEY UPDATE
  639. ";
  640. $tmpArrDps = array();
  641. if (empty($onDuplicate)) {
  642. $onDuplicate = $tableInfo;
  643. }
  644. $new_onDuplicate = $this->quote($onDuplicate);
  645. foreach ($new_onDuplicate as $tmpKey => $tmpV) {
  646. $tmpArrDps[] = " `{$tmpKey}` = {$tmpV} ";
  647. }
  648. $sql .= join(', ', $tmpArrDps);
  649. if (!$this->db->query($sql)) {
  650. return false;
  651. }
  652. $id = $this->db->insertId();
  653. # 清空这条记录的缓存
  654. self::deleteFromCache($id);
  655. return $id > 0 ? $id : true;
  656. }
  657. /**
  658. * 根据条件更新指定数据
  659. * @param array $tableInfo 待更新的数据(与数据库字段对应的数据)
  660. * @param array $condition 条件(与数据库字段对应的数据)
  661. * @return boolean
  662. */
  663. public function update(array $tableInfo, array $condition) {
  664. if(empty($tableInfo)) return false;
  665. $sql = "UPDATE {$this->tableName}
  666. SET
  667. ";
  668. $sqlSets = '';
  669. foreach($tableInfo as $key => $val) {
  670. if($sqlSets != '') $sqlSets .= ' ,';
  671. $sqlSets .= "
  672. `{$key}` = {$this->quote($val)}
  673. ";
  674. }
  675. $sql .= $sqlSets;
  676. $where = $this->parseCondition($condition);
  677. $sql .= "
  678. {$where}
  679. ;
  680. ";
  681. $tmpQueryResult = $this->db->query($sql);
  682. if ($tmpQueryResult) {
  683. if (isset($condition[$this->primaryKey])) {
  684. # 清空这条记录的缓存,条件里有可能是一批ids
  685. self::deleteFromCache($condition[$this->primaryKey]);
  686. } else {
  687. $this->deleteAllCaches();
  688. }
  689. }
  690. return $tmpQueryResult;
  691. }
  692. /**
  693. * 根据条件删除数据
  694. * @param Array|String $condition 条件
  695. * @return boolean
  696. */
  697. public function delete($condition) {
  698. $where = $this->parseCondition($condition);
  699. $sql = "DELETE FROM {$this->tableName}
  700. {$where}
  701. ;
  702. ";
  703. $tmpQueryResult = $this->db->query($sql);
  704. if ($tmpQueryResult) {
  705. if (isset($condition[$this->primaryKey])) {
  706. # 清空这条记录的缓存,条件里有可能是一批ids
  707. self::deleteFromCache($condition[$this->primaryKey]);
  708. } else {
  709. $this->deleteAllCaches();
  710. }
  711. }
  712. return $tmpQueryResult;
  713. }
  714. /**
  715. * 转义数据
  716. * @param mixed $data
  717. */
  718. public function quote($data) {
  719. return SqlHelper::escape($data, true);
  720. }
  721. /**
  722. * 判断给定的值是否是有效的自增主键值
  723. * @param mixid $pk
  724. * @return boolean
  725. */
  726. static protected function isValidPK($pk) {
  727. return SqlHelper::isValidPK($pk);
  728. }
  729. /**
  730. * 判断给定数组的某个key的值,是否是有效的自增主键值
  731. * @param array $arr
  732. * @param mixid $key
  733. * @return boolean
  734. */
  735. static protected function isValidPKWithArray(array $arr, $key) {
  736. return SqlHelper::isValidPKWithArray($arr, $key);
  737. }
  738. /**
  739. * 获取指定条件的记录数
  740. * @return int
  741. */
  742. public function totals($condtion = null){
  743. $result = $this->findBy($condtion, null, null, "count(*)");
  744. if (!$result) {
  745. return 0;
  746. }
  747. return (int) array_pop(array_pop($result));
  748. }
  749. }