MySQL数据权限的实现详情
知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个数据库开发实战,手把手教大家学习《MySQL数据权限的实现详情》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!
数据权限模型
上篇文章的数据模型是基于传统的RBAC模型来设计的,由于我们这里的应用场景不一样,所以这里的数据权限模型并没有严格按照上篇文章的方案来设计,但是万变不离其宗,核心原理还是相同的。
首先我来介绍一下我们最终实现的效果
实现效果
一个组件(可以理解成菜单)可以绑定多个授权维度,当给角色授权组件时可以给这个授权组件赋予不同维度的权限。
关于数据权限的授权维度有以下几个关键点需要仔细体会:
- 给一个角色勾选授权维度实际上是在限制这个角色所能看到的数据范围
- 任何一个授权维度勾选了"全部",相当于不限制此维度的权限。
- 如果一个角色勾选了客户群的全部 + A产品线,那么最终生成的sql 会是
where 产品线 in ('A产品线')
- 如果一个角色勾选了客户群的全部 + A产品线,那么最终生成的sql 会是
- 如果一个角色勾选了多个维度,维度之间用 AND 拼接
- 如果一个角色勾选了A客户群 + B产品线,那么最终生成的sql 会是
where 客户群 in('A客户群')AND 产品线 in ('B产品线')
- 如果一个角色勾选了A客户群 + B产品线,那么最终生成的sql 会是
- 一个用户可能有多个角色,角色之间用 OR 拼接
- 一个用户有两个角色:客户群总监,产品线经理。其中客户群总监角色拥有A客户群和B客户群的权限,产品线经理角色拥有A产品线权限,那么最终生成的sql会是
where 客户群 in ('A客户群','B客户群') OR 产品线 in ('A产品线')
- 一个用户有两个角色:客户群总监,产品线经理。其中客户群总监角色拥有A客户群和B客户群的权限,产品线经理角色拥有A产品线权限,那么最终生成的sql会是
当然我们业务场景中数据规则比较单一,全部使用
in
作为sql条件连接符,你们可以根据实际业务场景进行补充。
数据模型
最终的数据模型如下所示:
这里的组件大家完全可以理解成RBAC模型中的资源、菜单,只不过叫法不同而已。
数据权限表结构
下面是具体的表结构设计
授权维度表
CREATE TABLE `wb_dimension` ( `ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键', `DIMENSION_CODE` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '维度编码', `DIMENSION_NAME` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '维度名称', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='授权维度'
具体授权维度表(产品线)
CREATE TABLE `wb_dimension_proc_line` ( `ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键', `DIMENSION_CODE` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '维度编码', `PROC_LINE_CODE` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '产品线编码', `PROC_LINE_NAME` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '产品线名称', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='授权维度-产品线'
跟授权维度表实际是一个表继承的关系,由于每个授权维度的属性不一样,展现形式也不一样,所以分表存储。
组件路由表
CREATE TABLE `wb_route` ( `ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键ID', `COMPONENT_ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '组件ID', `ROUTE_URL` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '路由地址', `AUTHORIZATION_TYPE` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '授权方式:1 自定义,2 上下级授权, 3 范围授权', `AUTHORIZATION_DIMENSION` json DEFAULT NULL COMMENT '授权维度', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='组件路由' 复制代码
当组件属性授权方式为范围授权时在应用侧会强制要求选择具体的授权维度,如 产品线、客户群。
角色表
CREATE TABLE `wb_role` ( `ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键ID', `ROLE_CODE` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '角色CODE', `ROLE_NAME` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '角色名称', `IDENTITY_ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '身份ID' PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='角色表'
角色上有一个身份属性,多个角色可以归属同一个身份,方便对角色进行分类管理。
角色组件绑定表
CREATE TABLE `role_component_relation` ( `ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键ID', `ROLE_ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色ID', `COMPONENT_ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '组件ID', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='角色授权组件'
角色组件授权规则表(核心)
CREATE TABLE `wb_role_component_rule` ( `ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键', `ROLE_ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '角色ID', `COMPONENT_ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '组件ID', `RULE_CODE` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则编码', `RULE_NAME` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则名称', `RULE_CONDITION` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则条件', `RULE_VALUE` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则值', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='角色组件维度规则表'
数据权限的核心表,规则条件的取值为IN,规则值存储具体的维度编码,当在数据维度中选择 全部 时我们将规则值存储为ALL这个特殊值,方便后续生成SQL语句。
实现过程
- 自定义一个数据权限的注解,比如叫
DataPermission
- 在对应的资源请求方法,比如商机列表上添加自定义注解
@DataPermission
- 利用AOP抓取到用户对应角色的所有数据规则并进行SQL拼接,最终在SQL层面实现数据过滤。
代码实现
自定义数据权限注解
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) @Documented public @interface DataPermission { /** * 数据权限类型 * 1 上下级授权 2 数据范围授权 */ String permissionType() default "2"; /** * 配置菜单的组件路径,用于数据权限 */ String componentRoute() default ""; }
定义数据权限处理切面
@Aspect @Slf4j public class DataPermissionAspect { @Autowired private RoleComponentRuleService roleComponentRuleService; @Pointcut("@annotation(com.ifly.workbench.security.annotation.DataPermission)") public void pointCut() { } @Around("pointCut()") public Object around(ProceedingJoinPoint point) throws Throwable{ HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); //获取请求token String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); String userName = JwtUtil.getUsername(token); MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataPermission permissionData = method.getAnnotation(DataPermission.class); //获取授权方式 String permissionType = permissionData.permissionType(); //获取组件路由 String componentRoute = permissionData.componentRoute(); if (StringUtils.isNotEmpty(componentRoute)){ // 查找当前用户此组件下的所有规则 List<rolecomponentruledto> componentRules = roleComponentRuleService.getRoleComponentRule(userName, componentRoute); if(CollectionUtils.isNotEmpty(componentRules)){ DataPermissionUtils.installDataSearchConditon(request, componentRules); SysUserCacheInfo userInfo = buildCacheUser(userName); DataPermissionUtils.installUserInfo(request, userInfo); } } return point.proceed(); } private SysUserCacheInfo buildCacheUser(String userName) { SysUserCacheInfo info = new SysUserCacheInfo(); info.setSysUserName(userName); info.setOneDepart(true); return info; } }</rolecomponentruledto>
在AOP中获取当前用户、需要访问的组件中所有的数据规则,参考wb_role_component_rule
表设计,并将其放到Request作用域中。
数据权限工具类
public class DataPermissionUtils { public static final String COMPONENT_DATA_RULES = "COMPONENT_DATA_RULES"; public static final String SYS_USER_INFO = "SYS_USER_INFO"; /** * 往链接请求里面,传入数据查询条件 * @param request * @param componentRules */ public static void installDataSearchConditon(HttpServletRequest request, List<rolecomponentruledto> componentRules) { // 1.先从request获取MENU_DATA_AUTHOR_RULES,如果存则获取到LIST List<rolecomponentruledto> list = loadDataSearchCondition(); if (list==null) { // 2.如果不存在,则new一个list list = Lists.newArrayList(); } list.addAll(componentRules); // 3.往list里面增量存指 request.setAttribute(COMPONENT_DATA_RULES, list); } /** * 获取请求对应的数据权限规则 * */ @SuppressWarnings("unchecked") public synchronized List<rolecomponentruledto> loadDataSearchCondition() { return (List<rolecomponentruledto>) SpringContextUtils.getHttpServletRequest().getAttribute(COMPONENT_DATA_RULES); } public synchronized void installUserInfo(HttpServletRequest request, SysUserCacheInfo userinfo) { request.setAttribute(SYS_USER_INFO, userinfo); } }</rolecomponentruledto></rolecomponentruledto></rolecomponentruledto></rolecomponentruledto>
在Request中存储数据规则。
查询组件规则
public interface RoleComponentRuleService extends IService<rolecomponentrule> { /** * 根据 用户域账户和组件编码 获取组件对应的关系 * * @param userName 域账号 * @param componentCode 组件编码 * @return 用户的所有规则 */ List<rolecomponentruledto> getRoleComponentRule(String userName, String componentCode); }</rolecomponentruledto></rolecomponentrule>
@Service public class RoleComponentRuleServiceImpl extends ServiceImpl<rolecomponentrulemapper rolecomponentrule> implements RoleComponentRuleService { @Resource private RoleComponentRuleMapper roleComponentRuleMapper; /** * 根据 用户域账户和组件编码 获取组件对应的关系 * @param userName 域账号 * @param componentCode 组件编码 * @return 用户的所有规则 */ @Override public List<rolecomponentruledto> getRoleComponentRule(String userName, String componentCode) { return roleComponentRuleMapper.getRoleComponentRule(userName,componentCode); } }</rolecomponentruledto></rolecomponentrulemapper>
<select id="getRoleComponentRule" resulttype="com.ifly.vo.RoleComponentRuleDTO"> SELECT tab1.id, tab1.role_id, tab4.role_code, tab1.component_id, tab1.rule_code, tab1.rule_name, tab1.rule_condition, tab1.rule_value, tab4.identity_id FROM wb_role_component_rule tab1 LEFT JOIN user_role_relation tab2 ON tab2.role_id = tab1.role_id LEFT JOIN wb_component tab3 ON tab3.id = tab1.component_id LEFT JOIN wb_role tab4 ON tab4.id = tab1.role_id JOIN role_component_relation tab5 ON tab5.role_id = tab1.role_id AND tab5.component_id = tab1.component_id where tab2.user_account = #{userName} and tab3.component_code = #{componentCode} </select>
Controller调用
@ApiOperation(value = "服务BU-领导-总览") @GetMapping("opp/getLeaderOverviewSve") @DataPermission(componentRoute = "020202") public Result<salesprojoverviewsve> getLeaderOverviewSve(@RequestParam(name = "identityId") String identityId) { String permissionSql = RuleQueryGenerator.getPermissionSql(identityId); log.info("查服务BU-领导-总览-permissionSQL==" + permissionSql); return Result.OK(overviewSveService.getLeaderOverviewSve(permissionSql)); }</salesprojoverviewsve>
在controller的请求方法上加上自定义注解@DataPermission并指定组件编码,然后通过工具类生成SQL条件,最后将SQL条件传入service层进行处理。
构建数据权限SQL
@Slf4j @UtilityClass public class RuleQueryGenerator { private static final String SQL_AND = " and "; private static final String SQL_OR = " or "; private static final String SQL_JOINT = " (%s) "; /** * 获取带有数据权限的SQL * @param identityId 身份ID */ public String getPermissionSql(String identityId) { //------------------------获取当前身份的数据规则------------------------------------ List<rolecomponentruledto> conditionList = getCurrentIdentyPermission(identityId); if (CollectionUtils.isEmpty(conditionList)) { //没有权限 return "1 = 0"; } //存在权限 //对当前身份根据规则编码分组-去除不同角色中相同编码且规则值为ALL的规则 并根据角色id分组 Map<string list>> ruleMap = getRuleMapByRoleId(conditionList); StringBuilder sb = new StringBuilder(); String roleSql; if (MapUtils.isNotEmpty(ruleMap)) { //按角色拼接SQL for (Map.Entry<string list>> entry : ruleMap.entrySet()) { List<rolecomponentruledto> lists = entry.getValue(); // 同角色之间使用 AND roleSql = buildRoleSql(lists); //角色之间使用 OR if (StringUtils.isNotEmpty(roleSql)) { jointSqlByRoles(sb, roleSql); } } } return sb.toString(); } private static List<rolecomponentruledto> getCurrentIdentyPermission(String identityId) { //----------------------------获取所有数据规则----------------------------- List<rolecomponentruledto> roleRuleList = DataPermissionUtils.loadDataSearchCondition(); if(CollectionUtils.isEmpty(roleRuleList)){ return null; } //-----------------------------过滤掉不属于当前身份的规则----------------------------------- return roleRuleList.stream() .filter(item -> item.getIdentityId().equals(identityId)) .collect(Collectors.toList()); } /** * 构建单角色SQL */ private static String buildRoleSql(List<rolecomponentruledto> lists) { StringBuilder roleSql = new StringBuilder(); for (RoleComponentRuleDTO item : lists) { //如果出现全选 则 代表全部,不需要限定范围 if ("ALL".equals(item.getRuleValue())) { continue; } //将规则转换成SQL String filedSql = convertRuleToSql(item); roleSql.append(SQL_AND).append(filedSql); } return roleSql.toString(); } /** * 将单一规则转化成SQL,默认全部使用 In * ruleCode : area_test * ruleValue : 区域1,区域2,区域3 * @param rule 规则值 */ private static String convertRuleToSql(RoleComponentRuleDTO rule) { String whereCondition = " in "; String ruleValueConvert = getInConditionValue(rule.getRuleValue()); return rule.getRuleCode() + whereCondition + ruleValueConvert; } /** * IN字符串转换 * 区域1, 区域2, 区域3 --> ("区域1","区域2","区域3") * 江西大区 --> ("江西大区") */ private static String getInConditionValue(String ruleValue) { String[] temp = ruleValue.split(","); StringBuilder res = new StringBuilder(); for (String string : temp) { res.append(",'").append(string).append("'"); } return "(" + res.substring(1) + ")"; } /** * 拼接单角色的SQL * @param sqlBuilder 总的SQL * @param roleSql 单角色SQL */ private static void jointSqlByRoles(StringBuilder sqlBuilder, String roleSql) { roleSql = roleSql.replaceFirst(SQL_AND, ""); if (StringUtils.isEmpty(sqlBuilder.toString())) { sqlBuilder.append(String.format(SQL_JOINT, roleSql)); } else { sqlBuilder.append(SQL_OR).append(String.format(SQL_JOINT, roleSql)); } } /** * * 1. 对当前身份根据规则编码分组-去除不同角色中相同编码且规则值为ALL的规则 * 2. 对角色进行分组 * @param conditionList 数据规则 * @return 分组后的规则list */ private static Map<string list>> getRuleMapByRoleId(List<rolecomponentruledto> conditionList) { //--------过滤掉不属于当前身份的规则,并对条件编码进行分组----------------------------------- Map<string list>> conditionMap = conditionList.stream().collect(Collectors.groupingBy(RoleComponentRuleDTO::getRuleCode)); //--------相同编码分组中存在ALL的排除掉----------------------------------------------- List<rolecomponentruledto> newRoleRuleList = new ArrayList(); if (MapUtils.isNotEmpty(conditionMap)) { for (Map.Entry<string list>> entry : conditionMap.entrySet()) { boolean flag = true; List<rolecomponentruledto> lists = entry.getValue(); for (RoleComponentRuleDTO item : lists) { if ("ALL".equals(item.getRuleValue())) { flag = false; break; } } if (flag) { newRoleRuleList.addAll(lists); } } } if (CollectionUtils.isNotEmpty(newRoleRuleList)) { return newRoleRuleList.stream().collect(Collectors.groupingBy(RoleComponentRuleDTO::getRoleId)); } return Maps.newHashMap(); } }</rolecomponentruledto></string></rolecomponentruledto></string></rolecomponentruledto></string></rolecomponentruledto></rolecomponentruledto></rolecomponentruledto></rolecomponentruledto></string></string></rolecomponentruledto>
核心类,用于生成数据权限查询的SQL脚本。
Dao层实现
<select id="getLeaderOverviewSve" resulttype="com.ifly.center.entity.SalesProjOverviewSve"> SELECT <include refid="column_list"></include> FROM U_STD_ADS.LTC_SALES_PROJ_OVERVIEW_SVE <where><if test="permissionSql != null and permissionSql != ''"> ${permissionSql} </if></where></select>
Dao层接受service层传入已经生成好的sql语句,作为查询条件直接拼接在业务语句之后。
小结
以上,就是数据权限的实现过程,其实代码实现并不复杂,主要还是得理解其中的实现原理。如果你也有数据权限的需求,不妨参考一下。
今天关于《MySQL数据权限的实现详情》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于mysql的内容请关注golang学习网公众号!

- 上一篇
- MySQL数据库存储引擎介绍及数据库的操作详解

- 下一篇
- Canal监听MySQL的实现步骤
-
- 务实的向日葵
- 这篇技术贴太及时了,太详细了,很有用,码住,关注大佬了!希望大佬能多写数据库相关的文章。
- 2023-04-15 06:25:23
-
- 畅快的小鸭子
- 这篇技术文章真是及时雨啊,大佬加油!
- 2023-01-10 09:43:08
-
- 虚幻的小松鼠
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢作者大大分享文章!
- 2023-01-09 10:53:22
-
- 踏实的香氛
- 这篇博文太及时了,好细啊,感谢大佬分享,已加入收藏夹了,关注师傅了!希望师傅能多写数据库相关的文章。
- 2023-01-07 20:31:40
-
- 顺心的星星
- 很棒,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢作者大大分享文章内容!
- 2023-01-03 08:11:49
-
- 含蓄的柜子
- 太细致了,收藏了,感谢博主的这篇技术贴,我会继续支持!
- 2023-01-02 21:49:25
-
- 聪明的机器猫
- 这篇文章内容真及时,太全面了,赞 👍👍,已加入收藏夹了,关注作者了!希望作者能多写数据库相关的文章。
- 2022-12-30 10:23:55
-
- 数据库 · MySQL | 1天前 |
- MySQL设置中文界面,超简单教程来了!
- 332浏览 收藏
-
- 数据库 · MySQL | 1天前 | mysql 索引提示
- MySQL进阶必看!FORCE/USE/IGNOREINDEX用法大揭秘
- 182浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- 手把手教你写MySQL存储过程,小白也能轻松上手
- 163浏览 收藏
-
- 数据库 · MySQL | 1天前 | mysql group by
- MySQL分组查询优化:GROUPBY原理+索引优化超全解析
- 324浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL设置中文语言,轻松拥有中文界面
- 211浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL建库语句从入门到精通:创建数据库+设置字符集&排序规则(附实例)
- 176浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- 从零开始学MySQL数据库操作,小白轻松变大神!
- 496浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL插入日期到时间字段,轻松搞定日期格式
- 484浏览 收藏
-
- 数据库 · MySQL | 1天前 | mysql 数据压缩
- MySQL怎么实现高效压缩存储?表压缩+列式存储详细解读
- 272浏览 收藏
-
- 数据库 · MySQL | 1天前 | mysql JOIN优化
- MySQL优化JOIN操作:七大技巧教你提升关联查询速度
- 106浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL出现中文乱码?超详细解决方案一次性搞定
- 211浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL主从复制这样配!搞懂这些参数,replication稳了~
- 131浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 茅茅虫AIGC检测
- 茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
- 18次使用
-
- 赛林匹克平台(Challympics)
- 探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
- 50次使用
-
- 笔格AIPPT
- SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
- 57次使用
-
- 稿定PPT
- 告别PPT制作难题!稿定PPT提供海量模板、AI智能生成、在线协作,助您轻松制作专业演示文稿。职场办公、教育学习、企业服务全覆盖,降本增效,释放创意!
- 53次使用
-
- Suno苏诺中文版
- 探索Suno苏诺中文版,一款颠覆传统音乐创作的AI平台。无需专业技能,轻松创作个性化音乐。智能词曲生成、风格迁移、海量音效,释放您的音乐灵感!
- 57次使用
-
- go-cqhttp权限管理系统的实现代码
- 2022-12-28 405浏览
-
- MySQL实现数据更新的示例详解
- 2023-02-25 176浏览
-
- Go 实现 WebSockets和什么是 WebSockets
- 2022-12-29 335浏览
-
- MySQL数据操作管理示例详解
- 2022-12-30 443浏览
-
- 详解MySQL中数据类型和字段类型
- 2023-02-23 311浏览