primaryKey, 'extend', 'create_time', 'update_time', 'cas_token',// 通过这个字段,提供冲突检测机制(乐观锁),保证高并发下的数据安全 ); $this->real_field = array_merge($fixed_field, $this->other_field); } /** * * 添加一条记录 * @param array $info * @param string $actionType 动态类型,这个参数控制以下行为中的一种: create、replace、insertIgnore * * ## 当$actionType的值为parent::PARAM_CREATE_ACTION_ONDUPLICATE时,该值才有意义。## * !!! 无法通过指定 $onDuplicate 来修改存储在 extend 字段里的值,因为没法把已存在的 extend 的值取出来,做一个merge。 !!! * @param array $onDuplicate 如果重复时,需要更新的信息。如果不指定,则使用$tableInfo的值,即认为要全部更新 * ## * * @throws ParamsException actionType 参数非法 * @return int | boolean */ public function add(array $info, $actionType = parent::PARAM_CREATE_ACTION_INSERT, array $onDuplicate = array()) { if (!isset($info['create_time']) || !Verify::unsignedInt($info['create_time'])) { $info['create_time'] = time(); } if (!isset($info['update_time']) || !Verify::unsignedInt($info['update_time'])) { $info['update_time'] = time(); } $extend = array(); foreach ($info as $tmpK => $tmpV) { if (!in_array($tmpK, $this->real_field)) { $extend[$tmpK] = $tmpV; unset($info[$tmpK]); } } # 从0开始。本来打算从1开始的,但考虑到 继续类可能会绕过 add 方法而实现自身的增加记录方法,将 cas_token默认值设为,有助于简化 $info['cas_token'] = 0; $tableInfo = $info; $tableInfo['extend'] = serialize($extend); switch ($actionType) { case parent::PARAM_CREATE_ACTION_INSERT: $id = $this->create($tableInfo); break; case parent::PARAM_CREATE_ACTION_INSERT_IGNORE: $id = $this->insertIgnore($tableInfo); break; case parent::PARAM_CREATE_ACTION_REPLACE: $id = $this->replace($tableInfo); break; case parent::PARAM_CREATE_ACTION_ONDUPLICATE: if (empty($onDuplicate)) { $onDuplicate = $tableInfo; unset($onDuplicate['create_time']); unset($onDuplicate['cas_token']); } else { # 确保不允许修改 extend 字段里存储的值。因为没法把已存在的 extend 的值取出来,做一个merge。 foreach ($onDuplicate as $tmpK => $tmpV) { if (!in_array($tmpK, $this->real_field)) { unset($onDuplicate[$tmpK]); } } } $onDuplicate['update_time'] = $info['update_time']; $onDuplicate['cas_token'] = SqlHelper::wrapperNoQuote("`cas_token` + 1"); $id = $this->insertDuplicate($tableInfo, $onDuplicate); break; default: throw new ParamsException("invalid actionType"); } return $id; } /** * 修改 * !!!!!! * !!! $cas_token 提供了一种“检查并修改”机制,非常重要的优势, 在这样一个冲突检测机制(乐观锁)下, 我们才能保证高并发下的数据安全。 * !!!!!! * 注:$info 或 $condition 里,一定要有一个指定 key 为 $this->primaryKey 对应的值 * @param array $info * @param array $condition 条件 * @param int $cas_token 如果$cas_token不为null,会执行一个“检查并修改”的操作。因此,仅当 $cas_token 与数据库里的值一致时,才会修改记录。 * @return ResultWrapper 成功:修改并影响到了记录; * 失败:数据里含失败描述。 * 另:cas_token检查不通过时,返回'CAS_TOKEN_NOT_MATCH'。这个返回值并不是严格意义上的正确,当$condition指定了条件,并且未匹配时, * 也会返回这个值。 */ public function modify(array $info, array $condition = null, $cas_token = null) { if (!isset($info[$this->primaryKey])) { if (!isset($condition[$this->primaryKey])) { return ResultWrapper::fail("请指定要修改的记录"); } $info[$this->primaryKey] = $condition[$this->primaryKey]; } if (!Verify::unsignedInt($info[$this->primaryKey])) { return ResultWrapper::fail("请指定有效的记录"); } if (!isset($condition)) { $condition = array(); } $useMasterFlag = $this->db->beginUseMaster(); $oldSpecialSale = parent::get($info[$this->primaryKey]); $this->db->restore($useMasterFlag); if (!$oldSpecialSale) { return ResultWrapper::fail("不存在的记录:{$info[$this->primaryKey]}");; } # 找出待更新的扩展字段 $extend = unserialize($oldSpecialSale['extend']); foreach ($info as $tmpK => $tmpV) { if (!in_array($tmpK, $this->real_field)) { $extend[$tmpK] = $tmpV; unset($info[$tmpK]); } } $info['cas_token'] = SqlHelper::wrapperNoQuote('`cas_token` + 1'); $tableInfo = $info; $tableInfo['extend'] = serialize($extend); $tableInfo['update_time'] = time(); $condition[$this->primaryKey] = $info[$this->primaryKey]; if (Verify::naturalNumber($cas_token)) { $condition['cas_token'] = $cas_token; } $result = $this->update($tableInfo, $condition); if (!$result) { return ResultWrapper::fail("数据库update操作失败"); } $affectedRows = $this->db->affectedRows(); if (!Verify::unsignedInt($affectedRows)) { if (Verify::naturalNumber($cas_token)) { return ResultWrapper::fail("CAS_TOKEN_NOT_MATCH"); } else { return ResultWrapper::fail("NOT_MATCH"); } } return ResultWrapper::success(); } /** * 获取一条记录 * @param int $id 记录id * @param int $cas_token 引用传值,默认为 null * @return false | array */ public function get($id, & $cas_token = null) { if (!Verify::unsignedInt($id)) { return false; } $result = $this->gets(array($id)); if (!$result) { return false; } $tmpResult = array_pop($result); $cas_token = $tmpResult['cas_token']; return $tmpResult; } /** * 获取一批记录 * @param array $ids 记录id集 * @param array $cas_tokens 引用传值,默认为 null * @return false | array */ public function gets(array $ids, array & $cas_tokens = null) { $result = parent::gets($ids); if (!$result) { return $result; } foreach ($result as & $tmpV) { $tmpExtend = array(); $tmpExtend = unserialize($tmpV['extend']); unset($tmpV['extend']); # 将存在扩展字段里的信息提出来 if (is_array($tmpExtend)) foreach ($tmpExtend as $tmpKK => $tmpVV) { if (!in_array($tmpKK, $this->real_field)) { $tmpV[$tmpKK] = $tmpVV; } } $cas_tokens[$tmpV[$this->primaryKey]] = $tmpV['cas_token']; } return $result; } /** * * 获取所有id集 * @param string $order * @param mixed $limit * @return array */ public function getsIdsAll($order = null, $limit = null) { $ids = parent::getsAllIds($order, $limit); return $ids; } /** * * 获取指定时间$update_time后的最新id集 * @param mixed $limit * @return array */ public function getsLatestIdsByUpdateTime($update_time, $limit = null) { $order = 'update_time ASC'; $condition = array( 'update_time' => SqlHelper::addCompareOperator('>=', $update_time), ); return $this->findIdsBy($condition, $limit, $order); } }