TP5兼容达梦国产数据库
1.首先数据库安装,部署时需配置大小写不敏感
2.安装PHP达梦扩展,一定要是对应版本(兼容操作系统)的扩展,否则会出现各种报错。参考官方文档:https://eco.dameng.com/document/dm/zh-cn/app-dev/php_php_new.html,注意拷贝扩展时根据安装的php版本,拷贝对应的线程安全或非安全版本扩展。
3.进入框架兼容改造部分,Config/database.php配置:
'type' => 'dm',
'quote' => '"', //库名(模式名)引用符号" 或 ` 等
4.添加对应驱动文件:thinkphp/library/think/db/connector/Dm.php 和 thinkphp/library/think/db/builder/Dm.php,具体代码看项目
thinkphp/library/think/db/connector/Dm.php:
<?php
// +----------------------------------------------------------------------
// | Modified for Dameng Database
// +----------------------------------------------------------------------namespace think\db\connector;use PDO;
use think\Container;
use think\db\Connection;
use think\db\Query;/*** 达梦数据库驱动*/
class Dm extends Connection
{protected $builder = '\\think\\db\\builder\\Dm'; // 使用达梦的 SQL 构造器/*** 初始化* @access protected* @return void*/protected function initialize(){// 可以在这里扩展达梦数据库特有的功能}/*** 解析pdo连接的dsn信息* @access protected* @param array $config 连接信息* @return string*/protected function parseDsn($config){$dsn = 'dm:host=' . $config['hostname'] . ';port=' . $config['hostport'] . ';schema="' . $config['database'] . '"';if (!empty($config['charset'])) {$dsn .= ';charset=' . $config['charset'];}return $dsn;}/*** 取得数据表的字段信息* @access public* @param string $tableName* @return array*/public function getFields($tableName){list($tableName) = explode(' ', $tableName);if (strpos($tableName, '.')) {list($schema, $tableName) = explode('.', $tableName);}// 达梦数据库获取表结构$sql = "SELECT COLUMN_NAME, DATA_TYPE,DATA_DEFAULT, DATA_LENGTH, NULLABLEFROM DBA_TAB_COLUMNSWHERE TABLE_NAME = '{$tableName}'";$pdo = $this->query($sql, [], false, true);$result = $pdo->fetchAll(PDO::FETCH_ASSOC);$info = [];if ($result) {foreach ($result as $key => $val) {$val = array_change_key_case($val);$info[$val['column_name']] = ['name' => $val['column_name'],'type' => $val['data_type'],'notnull' => 'N' == $val['nullable'],'default' => $val['data_default'],'primary' => $val['column_name'] == 'id', //表主键基本都是id,为方便获取信息省略查询'autoinc' => $val['column_name'] == 'id', //表自增字段基本都是id,为方便获取信息省略查询];}}/** 查询主键及自增示例* -- 获取主键字段SELECTc.COLUMN_NAME AS pkFROMDBA_CONSTRAINTS conJOINDBA_CONS_COLUMNS c ON con.CONSTRAINT_NAME = c.CONSTRAINT_NAMEWHEREcon.TABLE_NAME = 'ss_logs_select'AND con.OWNER = 'self-service'AND con.CONSTRAINT_TYPE = 'P';-- 检查字段是否为自增字段select b.table_name,a.name COL_NAME from SYS.SYSCOLUMNS a,all_tables b,sys.sysobjects c where a.INFO2 & 0x01 = 0x01and a.id=c.id and c.name= b.table_name AND b.table_name='ss_logs'* */return $this->fieldCase($info);}/*** 取得数据库的表信息* @access public* @param string $dbName* @return array*/public function getTables($dbName = ''){// 达梦数据库使用 SHOW TABLES 语句获取表信息$sql = !empty($dbName) ? "SELECT TABLE_NAME FROM DBA_TABLES WHERE OWNER = '" . $dbName . "'" : 'SELECT TABLE_NAME FROM DBA_TABLES';$pdo = $this->query($sql, [], false, true);$result = $pdo->fetchAll(PDO::FETCH_ASSOC);$info = [];foreach ($result as $key => $val) {$info[$key] = current($val);}return $info;}/*** 获取数据表信息* @access public* @param mixed $tableName 数据表名 留空自动获取* @param string $fetch 获取信息类型 包括 fields type bind pk* @return mixed*/public function getTableInfo($tableName, $fetch = ''){if (is_array($tableName)) {$tableName = key($tableName) ?: current($tableName);}if (strpos($tableName, ',')) {// 多表不获取字段信息return false;} else {$tableName = $this->parseSqlTable($tableName);}// 修正子查询作为表名的问题if (strpos($tableName, ')')) {return [];}list($tableName) = explode(' ', $tableName);if (false === strpos($tableName, '.')) {$schema = $this->getConfig('database') . '.' . $tableName;} else {$schema = $tableName;}if (!isset(self::$info[$schema])) {// 读取缓存$cacheFile = Container::get('app')->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php';if (!$this->config['debug'] && is_file($cacheFile)) {$info = include $cacheFile;} else {$info = $this->getFields($tableName);}$fields = array_keys($info);$bind = $type = [];foreach ($info as $key => $val) {// 记录字段类型$type[$key] = $val['type'];$bind[$key] = $this->getFieldBindType($val['type']);if (!empty($val['primary'])) {$pk[] = $key;}}if (isset($pk)) {// 设置主键$pk = count($pk) > 1 ? $pk : $pk[0];} else {$pk = null;}self::$info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk];}//针对性处理字段名是desc这个关键词的情况if($fetch == "bind" && isset(self::$info[$schema][$fetch]['desc'])){self::$info[$schema][$fetch]['"desc"'] = self::$info[$schema][$fetch]['desc'];unset(self::$info[$schema][$fetch]['desc']);}return $fetch ? self::$info[$schema][$fetch] : self::$info[$schema];}/*** 查找单条记录* @access public* @param Query $query 查询对象* @return array|null|\PDOStatement|string* @throws DbException* @throws ModelNotFoundException* @throws DataNotFoundException*/public function find(Query $query){// 分析查询表达式$options = $query->getOptions();$pk = $query->getPk($options);$data = $options['data'];$query->setOption('limit', 1);if (empty($options['fetch_sql']) && !empty($options['cache'])) {// 判断查询缓存$cache = $options['cache'];if (is_string($cache['key'])) {$key = $cache['key'];} else {$key = $this->getCacheKey($query, $data);}$result = Container::get('cache')->get($key);if (false !== $result) {return $result;}}if (is_string($pk) && !is_array($data)) {if (isset($key) && strpos($key, '|')) {list($a, $val) = explode('|', $key);$item[$pk] = $val;} else {$item[$pk] = $data;}$data = $item;}$query->setOption('data', $data);// 生成查询SQL$sql = $this->builder->select($query);$query->removeOption('limit');$bind = $query->getBind();if (!empty($options['fetch_sql'])) {// 获取实际执行的SQL语句return $this->getRealSql($sql, $bind);}// 事件回调$result = $query->trigger('before_find');if (!$result) {// 执行查询$resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']);if ($resultSet instanceof \PDOStatement) {// 返回PDOStatement对象return $resultSet;}$result = isset($resultSet[0]) ? $resultSet[0] : null;}if (isset($cache) && $result) {// 缓存数据$this->cacheData($key, $result, $cache);}return $result;}/*** SQL性能分析* @access protected* @param string $sql* @return array*/protected function getExplain($sql){// 达梦数据库可能不支持 EXPLAIN,需要根据实际情况调整return [];}protected function supportSavepoint(){return true;}/*** 启动 XA 事务* @access public* @param string $xid XA 事务 ID* @return void*/public function startTransXa($xid){$this->initConnect(true);if (!$this->linkID) {return false;}// 达梦数据库使用 DBMS_XA 包启动 XA 事务$this->linkID->exec("CALL DBMS_XA.START('{$xid}')");}/*** 预编译 XA 事务* @access public* @param string $xid XA 事务 ID* @return void*/public function prepareXa($xid){$this->initConnect(true);// 达梦数据库使用 DBMS_XA 包结束并准备 XA 事务$this->linkID->exec("CALL DBMS_XA.END('{$xid}')");$this->linkID->exec("CALL DBMS_XA.PREPARE('{$xid}')");}/*** 提交 XA 事务* @access public* @param string $xid XA 事务 ID* @return void*/public function commitXa($xid){$this->initConnect(true);// 达梦数据库使用 DBMS_XA 包提交 XA 事务$this->linkID->exec("CALL DBMS_XA.COMMIT('{$xid}')");}/*** 回滚 XA 事务* @access public* @param string $xid XA 事务 ID* @return void*/public function rollbackXa($xid){$this->initConnect(true);// 达梦数据库使用 DBMS_XA 包回滚 XA 事务$this->linkID->exec("CALL DBMS_XA.ROLLBACK('{$xid}')");}
}
thinkphp/library/think/db/builder/Dm.php:
<?php
// +----------------------------------------------------------------------
// | Adapted for Dameng Database
// +----------------------------------------------------------------------namespace think\db\builder;use think\db\Builder;
use think\db\Expression;
use think\db\Query;
use think\Exception;/*** 达梦数据库驱动*/
class Dm extends Builder
{// 查询表达式解析protected $parser = ['parseCompare' => ['=', '<>', '>', '>=', '<', '<='],'parseLike' => ['LIKE', 'NOT LIKE'],'parseBetween' => ['NOT BETWEEN', 'BETWEEN'],'parseIn' => ['NOT IN', 'IN'],'parseExp' => ['EXP'],'parseRegexp' => ['REGEXP', 'NOT REGEXP'],'parseNull' => ['NOT NULL', 'NULL'],'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'],'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'],'parseExists' => ['NOT EXISTS', 'EXISTS'],'parseColumn' => ['COLUMN'],];protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%';protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';/*** 生成insertall SQL* @access public* @param Query $query 查询对象* @param array $dataSet 数据集* @param bool $replace 是否replace* @return string*/public function insertAll(Query $query, $dataSet, $replace = false){$options = $query->getOptions();// 获取合法的字段if ('*' == $options['field']) {$allowFields = $this->connection->getTableFields($options['table']);} else {$allowFields = $options['field'];}// 获取绑定信息$bind = $this->connection->getFieldsBind($options['table']);foreach ($dataSet as $k => $data) {$data = $this->parseData($query, $data, $allowFields, $bind);$values[] = '( ' . implode(',', array_values($data)) . ' )';if (!isset($insertFields)) {$insertFields = array_keys($data);}}$fields = [];foreach ($insertFields as $field) {$fields[] = $this->parseKey($query, $field);}return str_replace(['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],[$replace ? 'REPLACE' : 'INSERT',$this->parseTable($query, $options['table']),implode(' , ', $fields),implode(' , ', $values),$this->parseComment($query, $options['comment']),],$this->insertAllSql);}/*** 正则查询* @access protected* @param Query $query 查询对象* @param string $key* @param string $exp* @param mixed $value* @param string $field* @return string*/protected function parseRegexp(Query $query, $key, $exp, $value, $field){if ($value instanceof Expression) {$value = $value->getValue();}// REGEXP约等于达梦数据库parseRegexp(),若行不通也可以使用 LIKE 或其他方式替代return "parseRegexp({$key}, {$value})";}/*** 字段和表名处理* @access public* @param Query $query 查询对象* @param mixed $key 字段名* @param bool $strict 严格检测* @return string*/public function parseKey(Query $query, $key, $strict = false){if (is_numeric($key)) {return $key;} elseif ($key instanceof Expression) {return $key->getValue();}$key = trim($key);// 达梦数据库不支持 JSON 字段的 `->>` 或 `->` 语法if (strpos($key, '->') || strpos($key, '->>')) {throw new Exception('JSON field syntax is not supported in Dameng Database.');}if (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {list($table, $key) = explode('.', $key, 2);$alias = $query->getOptions('alias');if ('__TABLE__' == $table) {$table = $query->getOptions('table');$table = is_array($table) ? array_shift($table) : $table;}if (isset($alias[$table])) {$table = $alias[$table];}}/*if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { //desc这种关键词形式需加""的字段会导致匹配失败throw new Exception('not support data:' . $key);}*//*if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {$key = '"' . $key . '"'; // 达梦数据库使用双引号}*/if (isset($table)) {$key = $table . '.' . $key;}return $key;}/*** 生成查询SQL* @access public* @param Query $query 查询对象* @return string*/public function select(Query $query){$options = $query->getOptions();return str_replace(['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],[$this->parseTable($query, $options['table']),$this->parseDistinct($query, $options['distinct']),$this->parseField($query, $options['field']),$this->parseJoin($query, $options['join']),$this->parseWhere($query, $options['where']),$this->parseGroup($query, $options['group']),$this->parseHaving($query, $options['having']),$this->parseOrder($query, $options['order']),$this->parseLimit($query, $options['limit']),$this->parseUnion($query, $options['union']),$this->parseLock($query, $options['lock']),$this->parseComment($query, $options['comment']),$this->parseForce($query, $options['force']),],$this->selectSql);}/*** table分析* @access protected* @param Query $query 查询对象* @param mixed $tables 表名* @return string*/protected function parseTable(Query $query, $tables){$item = [];$options = $query->getOptions();foreach ((array) $tables as $key => $table) {if (!is_numeric($key)) {$key = $this->connection->parseSqlTable($key);$item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table);} else {$table = $this->connection->parseSqlTable($table);if (isset($options['alias'][$table])) {$item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]);} else {$item[] = $this->parseKey($query, $table);}}}return implode(',', $item);}/*** 随机排序* @access protected* @param Query $query 查询对象* @return string*/protected function parseRand(Query $query){// 达梦数据库不支持 MySQL 的 rand() 函数,可以使用其他方式替代return 'DBMS_RANDOM.VALUE';}
}
5.Application/common.php添加方法:
//判断是否使用的达梦数据库
function isDMBase()
{return config('database.type') == 'dm';
}
6.相关group 分组sql语句与南大通用类似,group by的字段必须和查询字段相对应,不能出现字段分组模棱两可的情况。
7.Sql条件中存在逻辑或使用||的,更换成OR。
8.sql语句中使用了date_format()的地方全部更换成公共方法getSqlDateFormat()
格式符参考”DM8 SQL.pdf”文件里格式符:
/*** 根据提供的参数返回计算日期时间的sql语句* 为兼容达梦等数据库语法差异* 支持达梦group by* @param $intval 日期时间偏差,必须严格按照格式传输例如: -1 day* @param $time 日期时间* @param $format 日期时间格式* @return string*/
function getSqlDateFormat($intval, $format = '', $time = '')
{if($time == ''){$time = 'NOW()';}elseif(substr($time, 0, 5) == 'left('){ //兼容sql语句left(date_field_name, len),如left(create_time,10)$time = $time;}else{$time = "'{$time}'"; //具体日期字符串}$format = $format ?: '%Y-%m-%d';if(isDMBase()){//达梦语法$time = $time == 'NOW()' ? 'SYSDATE' : $time;$intvalArr = explode(' ', $intval);if(count($intvalArr) != 2){ //必须严格按照格式传输例如: -1 dayreturn '';}$offset = $intvalArr[0]; //偏移量$unit = strtolower($intvalArr[1]); //取最后一个单位(day,hour,month等)$unitFormat = '';switch ($unit){case 'day':$unitFormat = 'DD';break;case 'month':$unitFormat = 'MM';break;case 'hour':$unitFormat = 'HH';break;case 'week':$unitFormat = 'WW';break;case 'year':$unitFormat = 'YYYY';break;}$format = str_replace(['%Y', '%m', '%d', '%H', '%u', '%i', '%s'], ['YYYY', 'MM', 'DD', 'HH24', 'WW', 'MI', 'SS'], $format);$sql = "TO_CHAR(DATEADD({$unitFormat}, {$offset}, {$time}), '{$format}')";}elseif(isKBASEDataBase()){//人大金仓语法if(substr($time, 0, 5) == 'left('){$leftParams = explode(',', substr($time, 5, -1));$time = 'to_timestamp(substring(' . $leftParams[0] . ' FROM 1 FOR ' . $leftParams[1] . '))';}else{$time = $time == 'NOW()' ? 'CURRENT_DATE' : 'TIMESTAMP ' . $time;}$intvalArr = explode(' ', $intval);if(count($intvalArr) != 2){ //必须严格按照格式传输例如: -1 dayreturn '';}$offset = intval($intvalArr[0]); //偏移量$sign = $offset < 0 ? '-' : '+'; //符号位$offset = abs($offset);$unit = strtolower($intvalArr[1]); //取最后一个单位(day,hour,month等)$intvalstr = $offset == 0 ? '' : " {$sign} INTERVAL '{$offset} {$unit}'";$format = str_replace(['%Y', '%m', '%d', '%H', '%u', '%i', '%s'], ['YYYY', 'MM', 'DD', 'HH24', 'IW', 'MI', 'SS'], $format);$sql = "TO_CHAR({$time}{$intvalstr},'{$format}')";}else{$sql = "DATE_FORMAT(DATE_ADD({$time}, INTERVAL {$intval}), '{$format}')";}return $sql;
}/*** 根据提供的参数返回计算日期时间的sql语句* 为兼容达梦等数据库语法差异* 注:达梦不支持group by DATE_FORMAT()* @param $intval 日期时间偏差,必须严格按照格式传输例如: -1 day* @param $time 日期时间* @param $format 日期时间格式* @return string*/
/*function getSqlDateFormat($intval, $format = '', $time = '')
{if($time == ''){$time = 'NOW()';}elseif(substr($time, 0, 5) == 'left('){ //兼容sql语句left(date_field_name, len),如left(create_time,10)$time = $time;}else{$time = "'{$time}'"; //具体日期字符串}$format = $format ?: '%Y-%m-%d';if(isDMBase()){//达梦语法$intvalArr = explode(' ', $intval);if(count($intvalArr) != 2){ //必须严格按照格式传输例如: -1 dayreturn '';}$offset = $intvalArr[0]; //偏移量$unit = strtolower($intvalArr[1]); //取最后一个单位(day,hour,month等)if($unit == 'week'){$offset = intval($offset) * 7;$unit = 'day';}$sql = "DATE_FORMAT(DATE_ADD({$time}, INTERVAL '{$offset}' {$unit}), '{$format}')";}else{$sql = "DATE_FORMAT(DATE_ADD({$time}, INTERVAL {$intval}), '{$format}')";}return $sql;
}*/
相关测试代码:
SELECT TO_CHAR(ADD_DAYS('2021-08-16', -1), 'YYYY-MM-DD');SELECT TO_CHAR(DATEADD(HH, -8, SYSDATE), 'YYYY-MM-DD HH24');SELECT TO_CHAR(DATEADD(DD, -1, SYSDATE), 'YYYY-MM-DD HH24');SELECT TO_CHAR(DATEADD(WW, -1, SYSDATE), 'YYYY-MM-DD HH24');SELECT TO_CHAR(SYSDATE, 'YYYY-MM-WW HH24');SELECT TO_CHAR(DATEADD(MM, -1, SYSDATE), 'YYYY-MM-DD HH24 MI SS');SELECT TO_CHAR(DATEADD(MM, -1, '2016-08-27'), 'YYYY-MM-DD HH24');SELECT TO_CHAR(DATEADD(DD, -0, left(create_time,18)), 'YYYY-WW') FROM ss_payment ORDER BY create_time desc LIMIT 1;SELECT TO_CHAR(DATEADD(WW, -1, SYSDATE), 'YYYY-MM-DD HH24') AS date;
后又查看达梦网站相关支持函数文档:函数 | 达梦技术文档
将TO_CHAR切换成了DATE_FORMAT(DATE_ADD(datetime, interval), format)
,同时要注意函数DATE_ADD在达梦和mysql中的差异,达梦中interval的数值必须用’’包含,且数值和符号(-+)之间不能存在空格,且达梦不支持“周”需手动进行转化
但后续因为DATE_FORMAT()不支持group by,又切换回TO_CHAR()了。
9.达梦数据库要求 SELECT 列表中的所有非聚合字段(如 dname、dcode、ddate)必须出现在 GROUP BY 子句中,同时字段不能是别名,支持函数分组如left(),修改所有group by date,其中date是函数计算结果的别名,将别名替换成计算表达式,同时后面要加上所有非聚合字段。如:
group by left(create_time,10),非聚合字段...
10.sql错误:试图修改自增列[id],将更新数据中的自增字段过滤掉
11.字段赋值时使用单引号,看似简单实则很多地方都容易出现。
12.更新数据时报错:SQLSTATE[HY000]: General error: -2007 第 1 行, 第 447 列[desc]附近出现错误:
语法分析出错
根据报错极有可能是desc关键字相同字段名导致的,这种情况根据测试需要将字段名用双引号引用,但又会触发报错:不支持的数据表达式:"desc",需要删除think\db\builder\Dm
里面的parseKey方法里的严格模式判断逻辑去掉。
13.插入数据时报错:SQLSTATE[HY000]: General error: -2007 第 1 行, 第 155 列[desc]附近出现错误:语法分析出错
以上错误同10情况类似,只不过换成了插入操作。将desc换成”desc”会触发严格模式检测到字段”desc”不存在,根据builder.php中报错位置代码,大概浏览db/Query.php相关方法,大概有几种方式实现设置非严格模式:setOption()、option()、strict()以及配置config/database里的”fields_strict”=>false(影响全局),为降低影响范围,选择在调用insert()前调用->strict(false)
其实最好的办法还是在数据表设计初期禁用以关键字作为字段名称,
原以为设置上就可以解决问题,报错确实是不报错了,但是desc数据也确实是没更新,经排查似乎是认为”desc”字段不存在给直接过滤掉了
跟踪代码,最后通过connector/Dm.php重写getTableInfo()方法,针对desc字段解析单独处理,实现了插入成功。
14.达梦不支持group by date_format()表达式,更换application/common.php中getSqlDateFormat()切换成TO_CHAR()形式,上面已提过。
15.将DATE_SUB(now(), INTERVAL 60 second)更改成DATE_SUB(now(), INTERVAL '60' second), 数值加单引号
16.检查表是否存在使用新添加的公共方法checkTableExists()
/*** @param $table 表名* @param $isFullName 是否为全名* @return mixed*/
function checkTableExists($table = '', $isFullName = 0)
{$tableName = $isFullName ? $table : Config('database.prefix') . $table;$sql = "SHOW TABLES LIKE '{$tableName}'";if(isDMBase()){$database = Config('database.database');$sql = "SELECT TABLE_NAMEFROM DBA_TABLESWHERE OWNER = '{$database}' AND TABLE_NAME LIKE '{$tableName}'";}elseif(isGBASEDataBase()){ //这里是南大通用的,没有的情况下可以忽略或删除$sql = "SELECT tablenameFROM pg_catalog.pg_tablesWHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'AND tablename LIKE '{$tableName}'";}$exists = \think\Db::query($sql);return $exists;
}
17.CONCAT()不支持单参数,所以单个参数需要把concat去掉
18.SHOW TABLES;查看所有表更换成SELECT TABLE_NAME FROM DBA_TABLES WHERE OWNER = '模式名';
综上,只是本项目遇到的兼容问题,遇到新问题还是推荐参考官网文档尤其是函数方面,进行测试修改。
相关文章:
TP5兼容达梦国产数据库
1.首先数据库安装,部署时需配置大小写不敏感 2.安装PHP达梦扩展,一定要是对应版本(兼容操作系统)的扩展,否则会出现各种报错。参考官方文档:https://eco.dameng.com/document/dm/zh-cn/app-dev/php_php_new…...
[leetcode]2302.统计得分小于k的子数组
1.题目 2.事例 3.数据规模 4.思路(滑动窗口) 4.1滑动窗口的定义 滑动窗口是一种在数组、字符串等序列数据结构上进行操作的算法技巧。以下是其定义及相关要素的详细介绍: 定义:滑动窗口可以理解为在一个序列上,用一…...
Linux网络编程:TCP多进程/多线程并发服务器详解
Linux网络编程:TCP多进程/多线程并发服务器详解 TCP并发服务器概述 在Linux网络编程中,TCP服务器主要有三种并发模型: 多进程模型:为每个客户端连接创建新进程多线程模型:为每个客户端连接创建新线程I/O多路复用&am…...
Nacos源码—1.Nacos服务注册发现分析二
大纲 1.客户端如何发起服务注册 发送服务心跳 2.服务端如何处理客户端的服务注册请求 3.注册服务—如何实现高并发支撑上百万服务注册 4.内存注册表—如何处理注册表的高并发读写冲突 2.服务端如何处理客户端的服务注册请求 (1)客户端自动发送服务注册请求梳理 (2)Nacos…...
设备指纹护航电商和金融反欺诈体系建设
众所周知,人的指纹具有唯一性,可以作为人的身份识别标识。对于设备而言,也有可以用于识别的特征。设备指纹是指可以用于唯一标识出某一设备的特征或者独特的设备标识,具有固定性、较难篡改性、唯一性等特质。 设备指纹是金融机构…...
FFmpeg源码学习---ffmpeg
1、ffmpeg源码主函数 ┌────────────────────┐ │ main() │ └─────────┬───────────┘ ↓ ┌────────────────────┐ │ 初始化 (日志/网络等) │ │ init_dynload() │ │ avf…...
leetcode 206. 反转链表
题目描述: 迭代法: /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode …...
NVIDIA新模型DAM-3B:描述一切,图像视频局部描述新突破
在数字时代,图像和视频内容爆炸式增长,如何让AI像人类一样精准描述画面中的特定区域,成为计算机视觉领域的核心挑战。传统模型要么丢失细节,要么缺乏上下文,而NVIDIA与UC Berkeley联合团队提出的DAM(Descri…...
7、langChain和RAG实战:基于LangChain和RAG的常用案例实战
PDF 文档问答ChatBot 本地上传文档 支持 pdf支持 txt支持 doc/docx问答页面 python环境 新建一个requirements.txt文件streamlit python-docx PyPDF2 faiss-cpu langchain langchain-core langchain-community langchain-openai然后安装相应的包pip install -r requirements.t…...
c++11: 类型转换
目录 一 C语言中的类型转换 二 . C强制类型转换 1. static_cast 2. reinterpret_cast 3. const_cast 4. dynamic_cast 三 explicit 关键字 一 C语言中的类型转换 在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配ÿ…...
Matlab自学笔记五十二:变量名称:检查变量名称是否存在或是否与关键字冲突
1.变量名称的命名规则 有效的变量名称以字母开头,后跟字母、数字或下划线,Matlab变量名称对字母大小写是区分的,A和a是不相同的变量,不能使用与Matlab关键字冲突的变量名称,例如if、end等,判断一个字符是不…...
西门子PLC结构化编程_水处理系统水泵多备多投
文章目录 前言一、功能概述二、程序编写1. 需求分析2. 编写运行时间累计功能块3. 创建自定义数据类型1. 时间排序数据类型2. 多备多投数据类型3. 多备多投切换数据类型 4. 编程1. 创建DB数据块1. 多备多投数据块2. 多备多投切换数据块 2. 创建FB功能块 三、程序调用总结 前言 …...
AutoGen 框架深度解析:构建多智能体协作的事件驱动架构
在当下多智能体(Multi-Agent)AI系统快速发展的背景下,AutoGen 作为微软研究院开源的编程框架,为构建可扩展、灵活且可调试的智能体协作应用提供了完备的工具与最佳实践。本文将从设计动机、核心架构、关键概念、安装与快速上手、典型场景、进阶特性、生态与扩展、最佳实践,…...
算法相关概念
1 算法概述 1.1 算法概念 算法是特定问题求解步骤的描述,也是独立存在的一种解决问题的思想和方法 对于算法而言,实现他的编程语言无关紧要,重要的是思想和方法!!! 公式:程序算法数据结构&a…...
《Astro 3.0岛屿架构让内容网站“脱胎换骨”》
内容优先的网站越来越成为主流。无论是新闻资讯、知识博客,还是电商产品展示,用户都希望能快速获取所需内容,这对网站的性能和体验提出了极高要求。而Astro 3.0的岛屿架构,就像是为内容优先网站量身定制的一把神奇钥匙,…...
Vue3 + Element-Plus + 阿里云文件上传
Element-Plus 阿里云文件上传 1、选择文件夹方法2、Chrome 浏览器查看 input typefile 元素上传的文件方法3、上传文件4、FormDataFormData 是什么创建 FormDataFormData 常用方法FormData 的实际应用性能与注意事项总结 1、选择文件夹方法 input typefile 元素想要上传文件夹…...
【Linux】第十一章 管理网络
目录 1.TCP/IP网络模型 物理层(Physical) 数据链路层(Date Link) 网络层(Internet) 传输层(Transport) 应用层(Application) 2. 对于 IPv4 地址&#…...
用vite动态导入vue的路由配置
在Vue应用中,通过路由可以实现不同页面之间的切换,同时也可以实现页面之间的传参和控制页面的显示与隐藏。但是我们在开发的过程中,会发现在路由配置中的路由配置和我们的项目结构高度重复,在我们修改页面文件结构时非常的麻烦与复…...
sources.list.d目录
sources.list可能大家很熟悉,是配置镜像链接的地方。 sources.list.d其实就是一个目录,在linux系统中.d后缀一般定义为一个目录,且很喜欢用这种方式。 这种方式有一个好处,就是修改不会影响到sources.list文件, 在这里…...
【C语言】文件操作
目录 一为什么使用文件 二什么是文件 程序文件 数据文件 文件名 二进制文件和文本文件? 三文件的打开与关闭 流的概念 标准流 文件指针 指针的声明 指针的初始化 四文件的打开与关闭 打开 fopen()函数 五总结: 前言: …...
静态库与动态库简介
静态库与动态库简介 基本概念 静态库 静态库是在编译链接阶段被直接整合到可执行文件中的代码集合。链接器会从静态库中提取程序所需的所有对象,并将它们复制到最终的可执行文件中。 特点: 可执行文件包含了所有代码,运行时无需外部依赖…...
02《小地图实时》Unity
创建一个新的项目 创建一个球体 作为主角 重命名为Player 在主角上创建空的子物体 重命名为MiniMapIcon 增加一个精灵图片 并设置为绿色 增加一个层(目的是在小地图中看的到 而在场景中看不到这个绿色Icon) 命名为MiniMap 在主摄像机中设置剔除遮罩Culli…...
【Redis】基础4:作为分布式锁
文章目录 1. 一些概念2. MySQL方案2.1 方案一:事务特性2.1.1 存在的问题2.1.2 解决方案 2.2 方案二:乐观锁2.3 方案三:悲观锁 3. Redis3.1 实现原理3.2 实现细节3.2.1 问题1:持有期间锁过期问题3.2.2 问题2:判断和释放…...
迭代器与生成器
目录 Iterator 的作用 Iterator 的遍历过程 Symbol.iterator方法 实现iterator接口的自定义类示例 Generator函数 迭代器对象的next方法的运行逻辑 迭代器对象除了具有next方法,还可以具有return方法。 Iterator 的作用 为各种数据结构,提供一个统…...
Python 实现的运筹优化系统数学建模详解(动态规划模型)
相关代码链接:https://download.csdn.net/download/heikediguoshinib/90713747?spm1001.2014.3001.5503 一、引言 在计算机科学与数学建模的广阔领域中,算法如同精密的齿轮,推动着问题的解决与系统的运行。当面对复杂的优化问题时&…...
miniconda在ARM64位芯片上面的安装
文章目录 前言一、特点二、适用场景三、下载安装及使用1.下载脚本文件2.安装命令3.常见用法 总结 前言 Miniconda 是一个轻量级的 Python 发行版,它是 Anaconda 的一个简化版本。Anaconda 是一个广泛使用的数据科学平台,包含了众多的 Python 包和工具&a…...
vue跨域问题总结笔记
目录 一、Websocket跨域问题 1.nginx配置 2.VUE CLI代理 3.env.development配置 4.nginx日志 5.解决 一、解决跨域的几种常用方法 1.Vue CLI代理 2.JSONP 3.WebSocket 4.NGINX解决跨域问题 6.Java解决跨域 二、Vue跨域问题详解 1. 什么是跨域 2. 跨域的例子 3.…...
自动驾驶领域专业词汇(专业术语)整理
以下是分类整理的自动驾驶领域专业词汇表,涵盖 AI、芯片、传感器、自动驾驶核心、辅助驾驶、安全、通信、车灯、泊车、测试标准 等类别: AI相关 缩写英文全称中文解释AIArtificial Intelligence人工智能,模拟人类智能的技术体系NNNeural Ne…...
说一下react更新的流程
beginWork 使用v-dom和current fiber去生成子节点的workInProgress Fiber 期间会执行函数组件、类组件、diff子节点 给我需要变更的节点,打赏effectTag 增placement 2 0010 删deletion 8 1000 改 update 4 0100 增和改 placementAndUpdate…...
C 语言函数指针与指针函数详解
一、引言 在 C 语言的编程世界中,函数指针和指针函数是两个既强大又容易混淆的概念。它们为 C 语言带来了更高的灵活性和可扩展性,广泛应用于回调函数、动态链接库、状态机等多种场景。深入理解和掌握函数指针与指针函数,对于提升 C 语言编程…...
政策支持与市场驱动:充电桩可持续发展的双轮引擎
随着全球能源转型加速,新能源汽车成为实现低碳交通的重要方向。然而,充电基础设施不足仍是制约其普及的关键瓶颈。当前,国际主流的充电桩运营模式包括政府推动、电网企业推动及汽车厂商推动三种模式,但单一模式均存在显著局限性。…...
在 Ubuntu 22.04 x64 系统安装/卸载 1Panel 面板
一、 1Panel 是什么? 1Panel 是一款基于 Go 语言开发的现代化开源服务器管理面板(类似宝塔面板),专注于容器化(Docker)和云原生环境管理,提供可视化界面简化服务器运维操作。 1. 1Panel主要功…...
dummy cli-tool ubuntu22.04使用
项目场景:dummy cli-tool ubuntu22.04使用 提示:这里简述项目相关背景:执行python3 run_shell.py时报错 例如:项目场景:示例:通过蓝牙芯片(HC-05)与手机 APP 通信,每隔 5s 传输一批传感器数据(不是很大) …...
厚铜板的镀前处理差异:工艺参数与成本影响
在现代电子设备中,厚铜电路板因其优异的导电性能和良好的热管理能力而备受青睐。生产过程中,对铜层进行电镀加厚是一个关键步骤,它涉及到一系列复杂的化学和物理过程。在进行电镀之前,必须对电路板进行适当的准备工作,…...
【C到Java的深度跃迁:从指针到对象,从过程到生态】第四模块·Java特性专精 —— 第十六章 多线程:从pthread到JMM的升维
一、并发编程的范式革命 1.1 C多线程的刀耕火种 C语言通过POSIX线程(pthread)实现并发,需要开发者直面底层细节: 典型pthread实现: #include <pthread.h> int counter 0; pthread_mutex_t lock PTHREAD…...
数据库学习笔记(十三)---存储过程
前言: 学习和使用数据库可以说是程序员必须具备能力,这里将更新关于MYSQL的使用讲解,大概应该会更新30篇,涵盖入门、进阶、高级(一些原理分析);这一篇存储过程,下一篇是存储函数;虽然MYSQL命令很多,但是自…...
JWT(JSON Web Token)源码分析
Java - JWT的简单介绍和使用 Java JWT:原理、机制及案例示范 什么是JWT? 1.1 JWT的基本概念 JWT(JSON Web Token)是一种用于在各方之间传递JSON格式信息的紧凑、URL安全的令牌(Token)。JWT的主要作用是验…...
Vue 3 中通过 createApp 创建的 app 实例的所有核心方法,包含完整示例、使用说明及对比表格
以下是 Vue 3 中通过 createApp 创建的 app 实例的所有核心方法,包含完整示例、使用说明及对比表格: 1. app.component() 作用:注册全局组件 参数: name:组件名称(字符串)componentÿ…...
Hadoop 单机模式(Standalone Mode)部署与 WordCount 测试
通过本次实验,成功搭建了 Hadoop 单机环境并运行了基础 MapReduce 程序,为后续分布式计算学习奠定了基础。 掌握 Hadoop 单机模式的安装与配置方法。 熟悉 Hadoop 环境变量的配置及 Java 依赖管理。 使用 Hadoop 自带的 WordCount 示例程序进行简单的 …...
线段树合并与分解
合并 #include <bits/stdc.h> using namespace std; #define asd(i,a,b) for(int ia;i<b;i) #define int long long const int inf 0x3f3f3f3f, N 1e5 5, Z 1e5; int n, m, fa[N], o[N][25], dep[N], tot, root[N], ans[N]; vector<int> g[N]; struct node…...
驱动开发硬核特训 │ 深度解析 fixed regulator 驱动与 regulator_ops
一、引言:本次目标 本篇聚焦于: Regulator 子系统基础概念设备树节点与驱动代码的对应关系regulator_desc、regulator_ops、regulator_dev 的完整讲解驱动端的实际注册与管理流程 通过一个实际案例,系统掌握 regulator 子系统 的全貌。 二…...
Linux中的shell脚本练习
1.判断字符串是否为空 #!/usr/bin/bash while : #:默认值为真 do read -p "请输入你的密码: " a pass123456 if [ -z $a ];thenecho "您输入的密码不能为空"exit 1 elseif [ $a $pass ];thenecho "登录成功"breakelseecho "您的密码输入有…...
MySQL基础篇 | 1-数据库概述与MySQL安装
【MySQL基础篇-1】数据库概述与MySQL安装 1. 数据库概述2. MySQL环境搭建2.1. MySQL的四大版本2.2. 软件下载1. 数据库概述 MySQL官网网站:https://dev.mysql.com/doc/relnotes/mysql/8.0/en/ SQL Server:SQL Server是微软开发的大型商业数据库。C#、.net等语言常使用,与wi…...
JVM 自动内存管理
一、运行时数据区域详解 Java 虚拟机在运行 Java 程序时,会将所管理的内存划分为多个不同的数据区域,各区域有着独特的用途、创建和销毁时间。 程序计数器:作为线程私有的较小内存空间,它是当前线程执行字节码的行号指示器。字节…...
InitializingBean接口和@PostConstruct-笔记
1. InitializingBean 简介 1.1 功能简介 InitializingBean 是 Spring 框架中的一个接口,用在 Bean 初始化后执行自定义逻辑。它提供了 afterPropertiesSet() 方法,该方法在以下时机被 Spring 容器自动调用: 属性注入完成后(即所…...
考研408-计算机组成原理冲刺考点(1-3章)
第一章 计算机系统概述 1.计算机核心 早期的冯诺依曼计算机是以运算器为中心的,而现在的计算机是以存储器为中心的 2.五大部件 3.汇编程序、编译程序、解释程序的辨析...
模板方法模式(Template Method Pattern)
模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个操作中的算法骨架,将一些步骤的实现延迟到子类中。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。 一、基础 1. 意图 定义一个操作中的算法骨架,将某些步骤延迟到…...
一文了解无人机系统
无人机系统,又称无人驾驶航空器系统(Remotely Piloted Aircraft System,RPAS),作为一个由无人机平台、遥控站、指令与控制数据链及其他部件构成的完整技术体系,其系统架构包含多个核心分系统。具体而言&…...
系统架构师2025年论文《论软件的设计模式》
论软件的设计模式 摘要: 2016 年,我所在的公司承担了某市医院预约挂号系统的研发任务。我作为公司的技术总监,希望能打造基于该系统的系列产品,参与到项目的设计中,以期开发扩展性和可维护性良好的预约挂号系统,为以后的产品开发打下基础。网络靶场是网络安全技术研究的…...
集成电路流片随笔19:full_handshake
全双工握手接收模块 (full_handshake_rx),它的功能是接收来自发送端 (tx) 的数据,并对发送端进行应答(ACK)。模块实现了基于握手的通信机制,以确保数据的可靠传输。模块的输入输出分别连接于发送端和接收端,…...