首页 文章 精选 留言 我的

精选列表

搜索[快速],共10000篇文章
优秀的个人博客,低调大师

MySQL中怎样快速找出超长索引

大家好,我是知数堂SQL 优化班老师 网名:骑龟的兔子 需求: 想要查找哪些索引太长了,这个SQL在5.7下跑的特别慢,8.0则挺快的,帮看下有啥优化方案没 具体SQL 和执行计划如下: SELECT c.TABLE_SCHEMA AS DB, c .TABLE_NAME AS TBL, c.COLUMN_NAME AS COL, c.CHARACTER_OCTET_LENGTH AS COL_LEN_BYTES, s.INDEX_NAME, s.SUB_PART * CHARACTER_OCTET_LENGTH/CHARACTER_MAXIMUM_LENGTH AS SUB_PART_LENFROM information_schema.COLUMNS c INNER JOIN information_schema.STATISTICS s USING(TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME)INNER JOIN information_schema.TABLES t USING(TABLE_SCHEMA, TABLE_NAME) WHERE c.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test')AND c.DATA_TYPE IN ("varchar", "char", "text", "blob") AND ((CHARACTER_OCTET_LENGTH > 50 and SUB_PART is null) orSUB_PART * CHARACTER_OCTET_LENGTH/CHARACTER_MAXIMUM_LENGTH > 50)AND t.TABLE_ROWS > 10000ORDER BY COL_LEN_BYTES DESC;执行计划*************************** 1. row *************************** id: 1 select_type: SIMPLE table: c partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_frm_only; Scanned all databases; Using temporary; Using filesort*************************** 2. row *************************** id: 1 select_type: SIMPLE table: s partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_frm_only; Scanned all databases; Using join buffer (Block Nested Loop)*************************** 3. row *************************** id: 1 select_type: SIMPLE table: t partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_full_table; Scanned all databases; Using join buffer (Block Nested Loop)3 rows in set, 1 warning (0.01 sec)select count(*) from information_schema.tables;+----------+| count(*) |+----------+| 33600 |+----------+select count(*) from information_schema.COLUMNS;+----------+| count(*) |+----------+| 342967 |+----------+select count(*) from information_schema.STATISTICS;+----------+| count(*) |+----------+| 135167 |+----------+ 上面的SQL 运行450+ s 也运行不出来,最后kill掉了。 我们初步分析一下,从执行计划中 可以看出三个表都是ALL所以很慢 那添加索引不就行了吗,因为是系统表,所以不能随便添加! 那该怎么办?想到了AUTOKEY 就是临时索引,那思路就是改写SQL 达到生成临时索引,最终达到优化效果 改写的SQL 如下 SELECT c.TABLE_SCHEMA AS DB, c.TABLE_NAME AS TBL, c.COLUMN_NAME AS COL, c.CHARACTER_OCTET_LENGTH AS COL_LEN_BYTES, s.INDEX_NAME,s.SUB_PART * c.CHARACTER_OCTET_LENGTH/c.CHARACTER_MAXIMUM_LENGTH AS SUB_PART_LENFROM ( select c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME ,c.CHARACTER_OCTET_LENGTH ,c.CHARACTER_MAXIMUM_LENGTH , c.DATA_TYPEfrom information_schema.COLUMNS c WHERE c.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') AND c.DATA_TYPE IN ("varchar", "char", "text", "blob") limit 10000000000) c INNER JOIN (select s.TABLE_SCHEMA, s.TABLE_NAME, s.COLUMN_NAME ,s.SUB_PART,s.INDEX_NAMEfrom information_schema.STATISTICS s WHERE s.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test')limit 10000000000 )s USING(TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME)INNER JOIN information_schema.TABLES t USING(TABLE_SCHEMA, TABLE_NAME) WHERE c.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') AND c.DATA_TYPE IN ("varchar", "char", "text", "blob") AND ((c.CHARACTER_OCTET_LENGTH > 50 and s.SUB_PART is null) ors.SUB_PART * c.CHARACTER_OCTET_LENGTH/c.CHARACTER_MAXIMUM_LENGTH > 50)AND t.TABLE_ROWS > 10000ORDER BY COL_LEN_BYTES DESC;*************************** 1. row *************************** id: 1 select_type: PRIMARY table: t partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_full_table; Scanned all databases; Using temporary; Using filesort*************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived2> partitions: NULL type: refpossible_keys: <auto_key0> key: <auto_key0> key_len: 388 ref: information_schema.t.TABLE_SCHEMA,information_schema.t.TABLE_NAME rows: 2 filtered: 50.00 Extra: Using where*************************** 3. row *************************** id: 1 select_type: PRIMARY table: <derived3> partitions: NULL type: refpossible_keys: <auto_key0> key: <auto_key0> key_len: 582 ref: information_schema.t.TABLE_SCHEMA,information_schema.t.TABLE_NAME,c.COLUMN_NAME rows: 2 filtered: 100.00 Extra: Using where*************************** 4. row *************************** id: 3 select_type: DERIVED table: s partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_frm_only; Scanned all databases*************************** 5. row *************************** id: 2 select_type: DERIVED table: c partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_frm_only; Scanned all databases5 rows in set, 1 warning (0.01 sec) 结果来了 2463 rows in set, 417 warnings (23.39 sec) 但是经过几次运行之后 有时候是40多秒有时候甚至达到了166s 非常不稳定! 那分析下上面这个SQL的问题在哪里? 问题就是生成的AUTO KEY的量相对来说非常大!因为没有进行任何过滤 那现在的思路就是对生成的AUTOKEY的量 进行减少 我们通过相对小的表TABLES表生成autokey 之后STATISTICS ,COLUMNS 表分别跟 TABLES 表进行JOIN 然后减少数据量 达到减少生成AUOKEY 的量 减少 达到优化目的 ,具体的方法如下 select count(1) from (select s.TABLE_SCHEMA, s.TABLE_NAME, s.COLUMN_NAME ,s.SUB_PART,s.INDEX_NAMEfrom information_schema.STATISTICS s WHERE s.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') )s straight_join (select t.TABLE_SCHEMA, t.TABLE_NAMEfrom information_schema.TABLES t WHERE t.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') AND t.TABLE_ROWS > 10000limit 10000000000) t on s.TABLE_SCHEMA=t.TABLE_SCHEMA and s.TABLE_NAME =t.TABLE_NAME*************************** 1. row *************************** id: 1 select_type: PRIMARY table: s partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_frm_only; Scanned all databases*************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived3> partitions: NULL type: refpossible_keys: <auto_key0> key: <auto_key0> key_len: 388 ref: information_schema.s.TABLE_SCHEMA,information_schema.s.TABLE_NAME rows: 2 filtered: 100.00 Extra: Using index*************************** 3. row *************************** id: 3 select_type: DERIVED table: t partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_full_table; Scanned all databases3 rows in set, 1 warning (0.00 sec)+----------+| count(1) |+----------+| 7478 |+----------+1 row in set, 40 warnings (7.52 sec)select count(1) from ( select c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME ,c.CHARACTER_OCTET_LENGTH ,c.CHARACTER_MAXIMUM_LENGTH , c.DATA_TYPEfrom information_schema.COLUMNS c WHERE c.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') AND c.DATA_TYPE IN ("varchar", "char", "text", "blob") ) c straight_join (select t.TABLE_SCHEMA, t.TABLE_NAMEfrom information_schema.TABLES t WHERE t.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') AND t.TABLE_ROWS > 10000limit 10000000000) t on c.TABLE_SCHEMA=t.TABLE_SCHEMA and c.TABLE_NAME =t.TABLE_NAME*************************** 1. row *************************** id: 1 select_type: PRIMARY table: c partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_frm_only; Scanned all databases*************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived3> partitions: NULL type: refpossible_keys: <auto_key0> key: <auto_key0> key_len: 388 ref: information_schema.c.TABLE_SCHEMA,information_schema.c.TABLE_NAME rows: 2 filtered: 100.00 Extra: Using index*************************** 3. row *************************** id: 3 select_type: DERIVED table: t partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_full_table; Scanned all databases3 rows in set, 1 warning (0.00 sec)+----------+| count(1) |+----------+| 8106 |+----------+1 row in set, 417 warnings (8.62 sec) 最终SQL 如下 SELECT c.TABLE_SCHEMA AS DB, c.TABLE_NAME AS TBL, c.COLUMN_NAME AS COL, c.CHARACTER_OCTET_LENGTH AS COL_LEN_BYTES, s.INDEX_NAME,s.SUB_PART * c.CHARACTER_OCTET_LENGTH/c.CHARACTER_MAXIMUM_LENGTH AS SUB_PART_LENfrom ( select c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME ,c.CHARACTER_OCTET_LENGTH ,c.CHARACTER_MAXIMUM_LENGTH , c.DATA_TYPEfrom information_schema.COLUMNS c WHERE c.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') AND c.DATA_TYPE IN ("varchar", "char", "text", "blob") ) c straight_join (select t.TABLE_SCHEMA, t.TABLE_NAMEfrom information_schema.TABLES t WHERE t.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') AND t.TABLE_ROWS > 10000limit 10000000000) t on c.TABLE_SCHEMA=t.TABLE_SCHEMA and c.TABLE_NAME =t.TABLE_NAMEstraight_join(select s.* from (select s.TABLE_SCHEMA, s.TABLE_NAME, s.COLUMN_NAME ,s.SUB_PART,s.INDEX_NAMEfrom information_schema.STATISTICS s WHERE s.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') )s straight_join (select t.TABLE_SCHEMA, t.TABLE_NAMEfrom information_schema.TABLES t WHERE t.TABLE_SCHEMA not in ('information_schema','performance_schema','mysql','sys', 'test') AND t.TABLE_ROWS > 10000limit 10000000000) t on s.TABLE_SCHEMA=t.TABLE_SCHEMA and s.TABLE_NAME =t.TABLE_NAMElimit 10000000000) s on c.TABLE_SCHEMA=s.TABLE_SCHEMA and c.TABLE_NAME=s.TABLE_NAME and c.COLUMN_NAME =s.COLUMN_NAMEwhere ((c.CHARACTER_OCTET_LENGTH > 50 and s.SUB_PART is null) ors.SUB_PART * c.CHARACTER_OCTET_LENGTH/c.CHARACTER_MAXIMUM_LENGTH > 50)*************************** 1. row *************************** id: 1 select_type: PRIMARY table: c partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_frm_only; Scanned all databases*************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived3> partitions: NULL type: refpossible_keys: <auto_key0> key: <auto_key0> key_len: 388 ref: information_schema.c.TABLE_SCHEMA,information_schema.c.TABLE_NAME rows: 2 filtered: 100.00 Extra: Using index*************************** 3. row *************************** id: 1 select_type: PRIMARY table: <derived4> partitions: NULL type: refpossible_keys: <auto_key0> key: <auto_key0> key_len: 582 ref: information_schema.c.TABLE_SCHEMA,information_schema.c.TABLE_NAME,information_schema.c.COLUMN_NAME rows: 2 filtered: 100.00 Extra: Using where*************************** 4. row *************************** id: 4 select_type: DERIVED table: s partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_frm_only; Scanned all databases*************************** 5. row *************************** id: 4 select_type: DERIVED table: <derived6> partitions: NULL type: refpossible_keys: <auto_key0> key: <auto_key0> key_len: 388 ref: information_schema.s.TABLE_SCHEMA,information_schema.s.TABLE_NAME rows: 2 filtered: 100.00 Extra: Using index*************************** 6. row *************************** id: 6 select_type: DERIVED table: t partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_full_table; Scanned all databases*************************** 7. row *************************** id: 3 select_type: DERIVED table: t partitions: NULL type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Using where; Open_full_table; Scanned all databases7 rows in set, 1 warning (0.00 sec) 看起来稳定了,跑了几次,都没超过15秒 我的新一轮的SQL 优化课 即将在春节后开课 我是知数堂SQL 优化班老师~ ^^ 如有关于SQL优化方面疑问和一起交流的请加 并且 @兔子@知数堂SQL优化 高性能MySQL,SQL优化群 有叶金荣,吴炳锡 两位大神坐镇 :579036588 欢迎加入 知数堂大家庭。 我的微信公众号:SQL开发与优化(sqlturning) 扫码直达宝藏课程 本文分享自微信公众号 - 老叶茶馆(iMySQL_WX)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

5G推动智慧医疗快速发展

近日,国家工信部和卫健委办公厅联合组织开展“5G+医疗健康”应用试点项目申报工作,围绕急诊救治、远程诊断、远程治疗、远程重症监护(ICU)、中医诊疗、医院管理、智能疾控、健康管理等8个重点方向,鼓励各地、各单位创新5G应用场景,加速卫生健康网络基础设施改造,推动5G+智慧医疗创新发展,引发高度关注。 近年来,我国医疗领域问题不断凸显,一方面医疗人力和设备资源有限且区域分布不均衡,不少人面临着“看病难、看病贵、看病慢”等困扰;另一方面,现有医疗技术水平、效率和服务也不高,医院运营成本压力较大。在此背景下,为满足行业发展需求,智慧医疗成为破局的关键所在,而5G的出现则为智慧医疗带来了催化作用。 凭借高速率、低延迟、大容量的显著特点,5G网络能够深入到医疗诊治和医院管理的各个环节,提升医疗水平,增强医院管理,同时与人工智能、VR/AR、机器人等智能技术和装备相结合,协作医生与管理人员,减轻治疗和作业压力。除此以外,5G还能通过医疗服务的信息化、移动化和远程化,加速医疗资源的共享与下沉。 总而言之,5G的出现与应用能催化智慧医疗的进一步发展。目前,根据工信部最新数据透露,我国5G基站建设已经突破60万座,提前完成全年建设目标。同时5G网络覆盖也已取得不错成果,5G用户累计突破1亿人次,国内5G商用进程已是初具成效。在此背景下,未来伴随试点应用的不断开展,5G与医疗融合将真正成熟。 不过,在此之前,我国也还需要克服三方面的困难:其一是加快5G基站建设,完善5G专用网络覆盖,降低5G应用成本与价格;其二是加速5G技术研究,提升5G技术应用的安全性和可靠性;其三是强化行业标准与监管,为行业应用营造健康、有序环境。只有解决了网络、技术和标准三方面难题,发展才能最终取得理想结果。

优秀的个人博客,低调大师

XiaoNuo 快速开发平台代码生成发布啦

小诺团队做开源已经将近一个月,在平时忙碌的工作之余跟晚上的时间,也在抓紧时间做开发。 在此时间段当然我们的其他产品也不能停下来,就好比:xiaonuo-layui版本的工作流大模块、xiaonuo-cloud版本,相信很快跟小伙伴们见面。 为了加快cloud版本日程,小诺团队成员也由2人增加到3人!同时也欢迎其他有想法的小伙伴加入到我们的开源中一起做贡献,可以在官网获取联系方式与我们沟通! 此次一共发布了xiaonuo-vue版本与xiaonuo-layui版本的代码生成器,完全独立的模块,可拓展,可二次开发,为普通模块开发省去了大部分拷贝粘贴的时间。 可生成所有查询框、查询条件根据不同的需求进行删减,下个版本会在此基础上面增加详细到每个字段在各个代码模块下的配置 感兴趣的小伙伴可以关注下我们,全系版本都免费,我们的官网是:https://xiaonuo.vip

优秀的个人博客,低调大师

Jest 单元测试快速上手指南

技术交流群: https://fiora.suisuijiang.com/原文链接: https://github.com/yinxin630/blog/issues/38 Jest[1] 是一款简单, 容易上手且功能十分强大的测试框架 安装 yarn add -D jest 使用 创建 test 目录, 添加 plus.spec.js 文件 describe('example',()=>{it('shouldequal2',()=>{expect(1+1).toBe(2);});}); 执行 yarn jest 或者 yarn jest test/plus.spec.js 运行测试用例 成功结果 失败结果 输出测试覆盖率 在根目录创建 jest.config.js 配置文件 module.exports={collectCoverage:true,}; 创建 plus.js 模块 module.exports=functionplus(a,b){returna+b;} 修改测试用例使用模块 constplus=require('../plus');describe('example',()=>{it('shouldequal2',()=>{expect(plus(1,1)).toBe(2);});}); 再次执行测试, 输出覆盖率如下 在浏览器中打开 coverage/lcov-report/index.html 可以浏览覆盖率结果页面 忽略部分文件或者代码行的覆盖率 修改 plus.ts 模块, 添加更多分支 exportdefaultfunctionplus(a:number,b:number){if(a+b>100){return0;}elseif(a+b<0){return0;}else{returna+b;}} 重新执行测试, 覆盖率输出结果 你可以完善测试用例, 或者可能有些文件(譬如 config)和代码分支并不需要测试, 可以将其在测试覆盖率结果中排除, 参考如下配置 忽略目录下所有文件 在 jest.config.js 中添加 collectCoverageFrom:['**/*.{ts,tsx}','!**/node_modules/**','!**/[directorypath]/**',], 以 ! 开头的表示忽略与其匹配的文件 忽略单个文件 在该文件顶部添加 /* istanbul ignore file */ 忽略一个函数, 一块分支逻辑或者一行代码 在该函数, 分支逻辑或者代码行的上一行添加 /* istanbul ignore next */ 支持 Typescript 执行 yarn add -D typescript ts-jest @types/jest 安装 typescript 和声明 并在 jest.config.js 中添加 preset: 'ts-jest' 将 plus.js 重命名为 plus.ts exportdefaultfunctionplus(a:number,b:number){returna+b;} 同样的, 将 plus.spec.js 重命名为 plus.spec.ts importplusfrom'../plus'describe('example',()=>{it('shouldequal2',()=>{expect(plus(1,1)).toBe(2);});}); 执行测试, 结果和之前一致 执行单测时不校验 ts 类型 有时你可能会希望不校验 ts 类型, 仅执行代码测试, 比如需要在 CI 中将类型校验和单元测试分为两个任务 在 jest.config.js 中添加如下内容 globals:{'ts-jest':{isolatedModules:true,},} 测试 React 组件 安装 react 依赖 yarn add react react-dom 和声明 yarn add -D @types/react安装 react 测试库 yarn add -D @testing-library/react @testing-library/jest-dom 添加 typescript 配置文件 tsconfig.json {"compilerOptions":{"target":"es2018","strict":true,"moduleResolution":"node","jsx":"react","allowSyntheticDefaultImports":true,"esModuleInterop":true,"lib":["es2015","es2016","es2017","dom"]},"exclude":["node_modules"]} 新增测试组件 Title.tsx import React from 'react';function Title() { return ( <h1>Title</h1> );}export default Title; 新增测试用例 test/Title.spec.tsx /** * @jest-environment jsdom */import React from 'react';import { render } from '@testing-library/react';import '@testing-library/jest-dom/extend-expect';import Title from '../Title';describe('Title', () => { it('should render without error', () => { const { getByText } = render(<Title />); const $title = getByText('Title'); expect($title).toBeInTheDocument(); });}); 执行 yarn jest test/Title.spec.ts 查看结果 处理静态资源引用 react 组件有时引用一些静态资源, 譬如图片或者 css 样式表, webpack 会正确的处理这些资源, 但是对 Jest 来讲, 这些资源是无法识别的 创建 Title.less 样式表 h1 { color: red;} 修改 Ttitle.tsx, 添加样式引用 import './Title.less'; 执行测试会报错 我们需要配置 transform 对其处理 在根目录创建 jest.transformer.js constpath=require('path');module.exports={process(src,filename){return`module.exports=${JSON.stringify(path.basename(filename))};`;},}; 这里是将资源文件名作为模块导出内容 修改 jest.config.js 添加如下配置 transform:{'\\.(less)$':'<rootDir>/jest.transformer.js',//正则匹配,处理less样式}, 然后重新执行测试就可以了 处理 css in js 如果你使用了类似 linaria[2] 这种 css in js 方案, 其中的 css 样式模板字符串是不支持运行时编译的 修改 Title.tsx import React from 'react';import { css } from 'linaria';const title = css` color: red;`;function Title() { return <h1 className={title}>Title</h1>;}export default Title; 运行测试会报错 linaria 是通过 babel 插件将其预编译为 class 名的, 这里可以 mock 一下 css 函数, 返回一个随机值作为 class 名 在根目录创建 jest.setup.js jest.mock('linaria',()=>({css:jest.fn(()=>Math.floor(Math.random()*(10**9)).toString(36)),})); 修改 jest.config.js 添加如下配置 setupFilesAfterEnv:['./jest.setup.js'], 重新执行测试就可以了 测试交互事件 新增 Count.tsx 组件 import React, { useState } from 'react';function Count() { const [count, updateCount] = useState(0); return ( <div> <span data-testid="count">{count}</span> <button data-testid="button" onClick={() => updateCount(count + 1)}> +1 </button> </div> );}export default Count; 新增 test/Count.spec.tsx 组件 /** * @jest-environment jsdom */import React from 'react';import { render, fireEvent } from '@testing-library/react';import '@testing-library/jest-dom/extend-expect';import Count from '../Count';describe('Count', () => { it('should render without error', () => { const { getByTestId } = render(<Count />); const $count = getByTestId('count'); const $button = getByTestId('button'); expect($count).toHaveTextContent('0'); fireEvent.click($button); expect($count).toHaveTextContent('1'); });}); 这里通过 testId 来查找元素, 使用 fireEvent[3] 触发 click 事件 测试函数调用 新增 Button.tsx 组件 import React from 'react';type Props = { onClick: () => void;};function Button({ onClick }: Props) { return <button onClick={onClick}>button</button>;}export default Button; 添加 test/Button.spec.tsx 测试用例 /** * @jest-environment jsdom */import React from 'react';import { render, fireEvent } from '@testing-library/react';import '@testing-library/jest-dom/extend-expect';import Button from '../Button';describe('Button', () => { it('should render without error', () => { const handleClick = jest.fn(); // mock 函数 const { getByText } = render(<Button onClick={handleClick} />); // 传递 props const $button = getByText('button'); fireEvent.click($button); expect(handleClick).toHaveBeenCalled(); // 期望其被调用 });}); 测试包含定时器的逻辑 //timer.tsletcache='cache';exportdefaultfunctiontimer(){setTimeout(()=>{cache='';},1000);returncache;} //test/timer.spec.tsimporttimerfrom'../timer'jest.useFakeTimers();//替代原生计时器describe('timer',()=>{it('shouldclearcacheaftertimerout',()=>{expect(timer()).toBe('cache');jest.advanceTimersByTime(1000);//让计时器前进1000msexpect(timer()).toBe('');})}) mock 依赖模块 要测试的模块可能依赖于其他模块或者第三方 npm 包的结果, 我们可以使用 Mock Functions[4] 对其进行 mock //test/mock.spec.tsimport{mocked}from'ts-jest/utils';importplusfrom'../plus';jest.mock('../plus');describe('mock',()=>{it('shouldreturnmockvalue',()=>{mocked(plus).(50);expect(plus(1,1)).toBe(50);});}); 还有官网 mock axios npm 模块的例子 https://jestjs.io/docs/en/mock-functions#mocking-modules mock 环境变量和命令行参数 有的模块会从环境变量和命令行参数取值, 并且可能是在模块初始化时获取的 //process.tsconst{env,argv}=process;exportfunctiongetEnvironmentValue(){returnenv.Value;}exportfunctiongetProcessArgsValues(){returnargv[2];} 这种情况我们需要在每个测试用例中, 使用动态 require 来运行时引入改模块, 并且设置其每次引入时删除 cache //test/process.spec.tsdescribe('mockprocess',()=>{beforeEach(()=>{jest.resetModules();});it('shouldreturnenvironmentvalue',()=>{process.env={Value:'value',};const{getEnvironmentValue}=require('../process');expect(getEnvironmentValue()).toBe('value');});it('shouldreturnprocessargsvalue',()=>{process.argv=['value'];const{getProcessArgsValues}=require('../process');expect(getProcessArgsValues()).toBe('value');});}); 参考资料 [1]Jest: https://jestjs.io/ [2]linaria: https://github.com/yinxin630/blog/issues/36 [3]fireEvent: https://testing-library.com/docs/dom-testing-library/api-events [4]Mock Functions: https://jestjs.io/docs/en/mock-function-api 本文分享自微信公众号 - 牧码的星星(gh_0d71d9e8b1c3)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

使用 minikube 快速上手 Kubernetes | v1.7.3

minikube 踩坑和填坑。 环境说明 宿主机的环境 操作系统:Windows 10 虚拟化:VirtualBox 6.0 说明:在 Win 10 系统里尝试过 Hyper-V,感觉不好用,所以还是回归到了 VirtualBox。如果你用的是 Hyper-V,或者是 MacOS/Linux 操作系统,中间部分流程应该也可以参考。 如果有疑问可以查看 官方文档。 版本 Minikube:1.7.3 Kubernetes:1.17.3 说明:目前(2020-03-15)minikube 最新的版本是 1.8.2。我前几天尝试的版本是 1.8.1,踩到了其中的大 bug。 从 1.8.0 版本开始,它在部署的时候需要下载一个 Preload tarball images 的镜像文件,大小有 500 MB。 这个文件安装过程中是先下载到 .minikube\cache\preloaded-tarball 目录中,并且支持断点续传。但是,当我把从其它地方下载完全的文件放到该目录中时,并没有效果。也就是说缓存实际上没用。 最新的 1.8.2 似乎修复了这个 bug。 Minikube 最新的 1.8.2 和 1.7.3 对应的 Kubernetes 版本都是 1.17.3。使用旧版本启动集群需要额外下载的软件大小是这个 preload image 的一半,而且鉴于这个功能现在还貌似不够稳定,所以 推荐 使用 1.7.3 版本的 minikube。 下载 使用 minikube 安装部署所需要下载(拉取)的东西分为以下几类: minikube 命令:从 GitHub 上下载 minikube 启动虚机所需 iso:安装过程中自动下载,通过配置 --iso-url 加速,会缓存到本地 kubernetes 集群部署所需镜像,安装过程中自动拉取,通过配置 --image-repository 加速 kubernetes 集群部署所需程序,安装过程中自动下载,没有配置项,但是会缓存到本地 kubernetes 宿主机(即 Windows)所需客户端程序,安装过程自动下载,没有配置项,但是会缓存 应用所需镜像:安装过程不涉及,但是可以通过 --registry-mirror 指定加速镜像 可缓存的文件 整理了可缓存下载的文件如下,它们在 minikube 部署集群的时候会自动下载,如果下载时间过长,可以考虑通过其它下载工具提前下载,保存到指定目录(具体下文会说到)。 kubectl.exe ,下载地址 这是 Windows 系统下的 kubectl 客户端程序,启动 dashboard 时需要。 不管是通过 minikube 哪个版本安装,这个文件都是必需的。 kubeadm, 下载地址 kubectl, 下载地址 kubelet, 下载地址 这是 Linux 系统下的程序,虚拟机内安装 Kubernetes 需要。1.7.3 的 minikube 需要分别下载。 preloaded-images-k8s-v1-v1.17.3-docker-overlay2.tar.lz4, 下载地址 这是 1.8.0 之后的 minikube 需要下载的文件 以上文件下载无需科学上网,但是要看情况,出现无法连接可以多试几次。 安装 minikube minikube 就是单个 .exe 文件,既可以直接下载 minikube-windows-amd64.exe 然后改名,也可以下载 minikube-installer.exe 安装。推荐后者,因为安装包的体积更小,而且使用更方便。 执行 minikube-installer.exe,这个安装过程非常简单。 默认安装路径是 C:\Program Files\Kubernetes\Minikube,安装程序会自动把该路径加入到 PATH 中。 运行 cmd 命令行,检查 minikube 的版本: > minikube version minikube version: v1.7.3 commit: 436667c819c324e35d7e839f8116b968a2d0a3ff 启动集群 注意:如果以前安装过,需要使用 minikube delete 命令把旧版本删除。 如果版本不一致或者出现莫名其妙的错误,可以把 C:\Users\你的用户名\.minikube 也删除。 minikube v1.7.3 执行: minikube start --vm-driver=virtualbox --image-mirror-country=cn --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers --iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.7.3.iso --registry-mirror=https://reg-mirror.qiniu.com 选项说明: --vm-driver 如果不写会自动检测,可选值 virtualbox, vmwarefusion, hyperv, vmware --image-mirror-country 需要使用的镜像镜像的国家/地区代码。留空以使用全球代码。对于中国大陆用户,请将其设置为 cn。 --image-repository 用来拉取 Kubernetes 集群所需镜像的仓库 --iso-url 下载 minikube 虚机安装所需的 iso 文件 --registry-mirror docker registry 的镜像源,集群安装后拉取镜像加速用,可以使用其它加速器地址 更多选项可以执行 minikube start --help 首先是下载 iso 镜像: * minikube 1.8.2 is available! Download it: https://github.com/kubernetes/minikube/releases/tag/v1.8.2 * To disable this notice, run: 'minikube config set WantUpdateNotification false' ! Microsoft Windows 10 Pro 10.0.18362 Build 18362 上的 minikube v1.7.3 * Using the virtualbox driver based on user configuration * 正在使用镜像存储库 registry.cn-hangzhou.aliyuncs.com/google_containers * 正在下载 VM boot image... > minikube-v1.7.3.iso: 30.81 MiB / 167.39 MiB 18.41% 830.43 KiB p/s ETA 2m 这时候会自动创建 C:\Users\你的用户名\.minikube 文件夹,该目录下有一个 cache 文件夹,下载的文件都会缓存在这里。 接下来是创建虚机: * 正在创建 virtualbox 虚拟机(CPUs=2,Memory=2000MB, Disk=20000MB)... * 正在 Docker 19.03.6 中准备 Kubernetes v1.17.3… 这里的虚机配置对应的选项: --cpu=2, --memory='2000mb',--disk-size='20000mb',可以自己指定。 这个阶段虚机内部开始拉取镜像,所以会花一点时间,可以另起一个命令行,执行: > minikube ssh _ _ _ _ ( ) ( ) ___ ___ (_) ___ (_)| |/') _ _ | |_ __ /' _ ` _ `\| |/' _ `\| || , < ( ) ( )| '_`\ /'__`\ | ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )( ___/ (_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____) $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy v1.17.3 ae853e93800d 4 weeks ago 116MB registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver v1.17.3 90d27391b780 4 weeks ago 171MB registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager v1.17.3 b0f1517c1f4b 4 weeks ago 161MB registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler v1.17.3 d109c0821a2b 4 weeks ago 94.4MB registry.cn-hangzhou.aliyuncs.com/google_containers/dashboard v2.0.0-beta8 eb51a3597525 3 months ago 90.8MB registry.cn-hangzhou.aliyuncs.com/google_containers/coredns 1.6.5 70f311871ae1 4 months ago 41.6MB registry.cn-hangzhou.aliyuncs.com/google_containers/etcd 3.4.3-0 303ce5db0e90 4 months ago 288MB registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-scraper v1.0.2 3b08661dc379 4 months ago 40.1MB registry.cn-hangzhou.aliyuncs.com/google_containers/pause 3.1 da86e6ba6ca1 2 years ago 742kB registry.cn-hangzhou.aliyuncs.com/google_containers/storage-provisioner v1.8.1 4689081edb10 2 years ago 80.8MB $ 回到安装所在的命令行,可以看到需要下载的程序: 如果感觉半天没动静了,可以按一下回车 * 正在 Docker 19.03.6 中准备 Kubernetes v1.17.3… * 正在下载 kubelet v1.17.3 * 正在下载 kubectl v1.17.3 * 正在下载 kubeadm v1.17.3 下载后的文件仍然是保存在 C:\Users\你的用户名\.minikube\cache\linux\v1.17.3 文件夹中,如果这里下载的较慢,可以: 提前下载好对应的文件 此处按下 Ctrl-C 中断 minikube 把文件拷贝到对应的文件夹下 重新执行上面的 minikube start minikube 会从中断处继续往下执行。 * 正在启动 Kubernetes ... * Enabling addons: default-storageclass, storage-provisioner * 等待集群上线... * 完成!kubectl 已经配置至 "minikube" * 为获得最佳结果,请安装 kubectl:https://kubernetes.io/docs/tasks/tools/install-kubectl/ 安装完成,来看看集群的状态: > minikube kubectl get nodes * 正在下载 kubectl.exe v1.17.3 这里又要下载 Windows 系统下的 kubectl 客户端程序,下载后的文件保存在 C:\Users\你的用户名\.minikube\cache\windows\v1.17.3 文件夹中,如果这里下载的较慢,仍然可以提前下载好拷贝过去。 > minikube kubectl get nodes NAME STATUS ROLES AGE VERSION minikube Ready master 11m v1.17.3 就绪状态,开始启动 dashboard: > minikube dashboard X kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/ 这里提示我们没有在环境变量 PATH 中找到 kubectl,只需要将上面下载下来的 kubectl.exe 拷贝到任何一个已经在 PATH 中的目录即可。为了方便管理,我们可以把它和 minikube.exe 放一起。 > minikube dashboard * 正在开启 dashboard ... * Verifying dashboard health ... * Launching proxy ... * Verifying proxy health ... * Opening http://127.0.0.1:54032/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser... 此时命令行会一直阻塞在这里,自动打开浏览器,进入 Dashboard: 对于手动部署过 dashboard 的人来说,这个确实太方便了 并且后续可以直接使用 kubectl 命令来操作集群: > kubectl get pods -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-7f9c544f75-kw6g9 1/1 Running 0 29m kube-system coredns-7f9c544f75-p84v8 1/1 Running 0 29m kube-system etcd-minikube 1/1 Running 0 29m kube-system kube-apiserver-minikube 1/1 Running 0 29m kube-system kube-controller-manager-minikube 1/1 Running 0 29m kube-system kube-proxy-4jf76 1/1 Running 0 29m kube-system kube-scheduler-minikube 1/1 Running 0 29m kube-system storage-provisioner 1/1 Running 1 29m kubernetes-dashboard dashboard-metrics-scraper-7b64584c5c-x7bwz 1/1 Running 0 14m kubernetes-dashboard kubernetes-dashboard-79d9cd965-lq7xj 1/1 Running 0 14m 直接 SSH 连接集群 在命令行执行 minikube ssh 可以进入到虚机内部,但是因为终端类型的原因,会出现乱码,比如执行 top 命令: 所以最好还是通过模拟终端工具(Xshell/SecureCRT等)登入, 先执行 minikube ip 查询到虚机的 IP 地址 SSH 用户名选择为 docker 认证方式选择为 Public Key, 选择对应的私钥文件,C:\Users\你的用户名\.minikube\machines\minikube\id_rsa 附:最新 minikube v1.8.2 安装说明 执行: minikube start --driver=virtualbox --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers --registry-mirror=https://reg-mirror.qiniu.com --iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.8.0.iso --driver: 原 --vm-driver 改为 --driver --image-mirror-country: 带上该选项会报错,不带也没影响 --image-repository:一样 --iso-url 注意版本号 --registry-mirror: 同上 安装过程中不再需要下载那 3 个文件,改为下载 preloaded-images-k8s-v1-v1.17.3-docker-overlay2.tar.lz4 的镜像文件,大小 500MB,保存在 C:\Users\你的用户名\.minikube\cache\preloaded-tarball。 支持断点续传,并且可以提前把下载好的文件拷贝过去。 其它步骤和 v1.7.3 一样,不再赘述。 文章内容虽基础,整理发布不轻松 如果看过有帮助,不妨 点赞 + 关注,谢谢!

优秀的个人博客,低调大师

借助URLOS快速安装MixPHP-2.0.1框架

环境需求 最低硬件配置:1核CPU,1G内存(1+1)提示:如果你的应用较多,而主机节点的硬件配置较低,建议在部署节点时开通虚拟虚拟内存; 生产环境建议使用2G或以上内存; 推荐安装系统:Ubuntu-16.04、Ubuntu-18.04、CentOS7.X、Debian9X的64位的纯净的操作系统; URLOS安装 curl -LO www.urlos.com/iu && sh iu MixPHP安装流程 登录URLOS系统后台,在应用市场中搜索“MixPHP”,找到之后,直接点击安装按钮 填写服务名称、选择运行节点、服务端口、选择智能部署 填写网站域名、ssh密码(这里的密码是root密码) 然后点击“提交”按钮,等待部署完成; 访问网站访问:www.aaa.com:8088(这里是自己的域名) 登录sftp 网站根目录在 data/www/mix/

优秀的个人博客,低调大师

白盒测试快速入门1-简介

什么是白盒测试? 白盒测试被定义​​为测试软件解决方案的内部结构,设计和编码。在这种类型的测试中,代码对测试人员是可见的。它主要侧重于通过应用程序验证输入和输出的流程,改进设计和可用性,加强安全性。白盒测试也称为透明盒测试,开箱测试,结构测试,基于代码的测试和玻璃盒测试。它通常由开发人员执行。 它是软件测试“盒子测试”方法的两个部分之一。其对应的黑盒测试涉及从外部或最终用户类型的角度进行测试。另一方面,白盒测试基于应用程序的内部工作,并围绕内部测试。 由于透视框概念,使用了术语“WhiteBox”。透明框或WhiteBox名称表示能够透过软件的外壳(或“盒子”)进入其内部工作。同样,“黑匣子测试”中的“黑匣子”表示无法看到软件的内部工作方式,因此只能测试最终用户体验。 白盒测试中验证什么? 白盒测试涉及测试以下软件代码: 内部安全漏洞 编码过程中的路径损坏或结构不良 特定输入流 预期输出 条件循环 单独测试每个语句,对象和函数 测试可以在系统,集成和单元级别的软件开发中完成。白盒测试的基本目标之一是验证应用程序的工作流程。它涉及针对预期或期望的输出测试一系列预定义输入。 如果视频无法访问,请单击此处 如何进行白盒测试? 步骤1)理解源代码 测试人员经常做的第一件事就是学习并理解应用程序的源代码。由于白盒测试涉及测试应用程序的内部工作,因此测试人员必须非常了解他们正在测试的应用程序中使用的编程语言。此外,测试人员必须高度了解安全编码实践。安全性通常是测试软件的主要目标之一。 步骤2)创建测试用例并执行 白盒测试的第二个基本步骤涉及测试应用程序的源代码以获得正确的流程和结构。一种方法是编写更多代码来测试应用程序的源代码。测试人员将针对应用程序中的每个过程或一系列过程开发少量测试。此方法要求测试人员必须熟悉代码,并且通常由开发人员完成。 白盒测试示例 考虑以下代码 WhiteBox测试的目标是验证代码中的所有决策分支,循环和语句。 要运用上面代码中的语句,WhiteBox测试用例就是 a = 1,b = 1 a= -1,b = -3 白盒测试技术 主要的白盒测试技术是代码覆盖率分析。有自动化工具可用于执行代码覆盖率分析。以下是一些覆盖分析技术 语句覆盖: - 至少对代码中的每个可能的语句进行一次测试。 分支覆盖 - 此技术检查软件应用程序的每个可能路径(if-else和其他条件循环)。 除此之外,还有许多覆盖类型,例如条件覆盖,多条件覆盖,路径覆盖,功能覆盖等。每种技术都有其自身的优点,并尝试测试(覆盖)软件代码的所有部分。使用Statement和Branch覆盖,您通常可以获得80-90%的代码覆盖率,这已足够。 白盒测试的类型 白盒测试包含几种用于评估应用程序,代码块或特定软件包的可用性的测试类型。下面列出了 - 单元测试 它通常是在应用程序上完成的第一种类型的测试。在开发时,对每个单元或代码块执行单元测试。单元测试基本上由程序员完成。作为一名软件开发人员,您需要开发几行代码,一个函数或一个对象,并在软件开发生命周期早期继续进行单元测试以帮助识别大多数错误之前对其进行测试以确保其正常工作。在这个阶段确定的错误更便宜,易于修复。 内存泄漏测试 内存泄漏是运行速度较慢的应用程序的主要原因。如果您的软件应用程序运行缓慢,那么在检测内存泄漏方面经验丰富的QA专家非常重要。 除此之外,一些测试类型是黑盒和白盒测试的一部分。它们列出如下 白盒渗透测试 在此测试中,测试人员/开发人员拥有应用程序源代码,详细网络信息,涉及的IP地址以及运行应用程序的所有服务器信息的完整信息。目的是从多个角度攻击代码以暴露安全威胁 白盒Mutation测试: Mutation测试通常用于发现用于扩展软件解决方案的最佳编码技术。 参考资料 python测试开发项目实战-目录 python工具书籍下载-持续更新 python 3.7极速入门教程 - 目录 本文涉及的python测试开发库 谢谢点赞! 本文相关海量书籍下载 白盒测试工具 比如 pytest nose unittest doctest Veracode EclEmma RCUNIT NUnit JSUnit JUnit CppUnit gtest 白盒测试的优点 通过查找隐藏错误来优化代码。 白盒测试案例可以轻松实现自动化。 由于通常涵盖所有代码路径,因此测试更加彻底。 即使GUI不可用,也可以在SDLC中尽早开始测试。 WhiteBox测试的缺点 白盒测试可能非常复杂且昂贵。 通常执行白盒测试用例的开发人员会厌恶它。开发人员测试的白盒测试不详细可能导致生产错误。 白盒测试需要专业资源,并对编程和实现有详细的了解。 白盒测试非常耗时,更大的编程应用程序需要时间来完全测试。 小结 白盒测试可能非常复杂。所涉及的复杂性与正在测试的应用程序有很大关系。执行单个简单操作的小型应用程序可以在几分钟内进行白盒测试,而较大的编程应用程序需要数天,数周甚至更长时间才能完全测试。 白盒测试应该在软件应用程序上进行,因为软件应用程序在编写后会在每次修改后再次开发。

优秀的个人博客,低调大师

一文MyBatis-Plus快速入门

一、依赖及配置 使用下面的SQL创建数据库与添加数据 DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) ); DELETE FROM user; INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com'); 1、在idea中创建一个SpringBoot项目,在pom.xml中添需要的依赖 添加MyBatis-Plus、mysql连接驱动、lombok的依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> 2、配置数据库连接 application.yml spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mp username: root password: 1234 3、在启动类中添加注解 ==@MapperScan== 扫描Mapper接口包 @SpringBootApplication @MapperScan("com.jikedaquan.study.mp.mapper") public class MpApplication { public static void main(String[] args) { SpringApplication.run(MpApplication.class, args); } } 4、编写实体类,使用lombok @Data public class User { private Long id; private String name; private Integer age; private String email; } 5、编写UserMapper接口 UserMapper接口继承MyBatis-Plus提供的BaseMapper接口即可拥有CRUD的方法,泛型中填写操作的实体类,这里为User public interface UserMapper extends BaseMapper<User> { } 6、测试查询数据 @RunWith(SpringRunner.class) @SpringBootTest public class MpApplicationTests { @Autowired private UserMapper userMapper; @Test public void contextLoads() { List<User> userList = userMapper.selectList(null);//条件为null时查询所有数据 userList.forEach(System.out::println); } } 二、日志配置 配置日志到控制台输出 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 三、主键生成策略 MyBatis-Plus提供了多种主键生成策略以应对不同的场景 策略 说明 AUTO 数据库ID自增 NONE 该类型为未设置主键类型 INPUT 用户输入ID,该类型可以通过自己注册自动填充插件进行填充 ID_WORKER 全局唯一ID (idWorker) UUID 全局唯一ID (UUID) ID_WORKER_STR 字符串全局唯一ID (idWorker 的字符串表示) 1、注解控制主键生成策略 在实体类的主键字段上添加注解(自增时注意配合数据库设置) @Data public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; } 2、全局配置控制主键生成策略 application.yml mybatis-plus: global-config: db-config: id-type: id_worker 四、自动填充 在常用业务中有些属性需要配置一些默认值,MyBatis-Plus提供了实现此功能的插件。在这里修改user表添加 create_time 字段和 update_time 字段,在User类中添加对应属性。 1、为需要自动填充的属性添加注解 ==@TableField== 提供了4种自动填充策略:DEFAULT,默认不处理。INSERT,插入填充字段。UPDATE,更新填充字段。INSERT_UPDATE,插入和更新填充字段。 @Data public class User { private Long id; private String name; private Integer age; private String email; @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; } 2、实现字段填充控制器,编写自定义填充规则 实现 MetaObjectHandler 接口,实现 insertFill 和 updateFill 方法,此处的 create_time 和update_time字段需要插入时填充值, 只有 update_time 字段在修改时需要填充,所以策略如下。 //需要将自定义填充控制器注册为组件 @Component public class MyMetaObjectHandler implements MetaObjectHandler { private static final Logger LOGGER= LoggerFactory.getLogger(MyMetaObjectHandler.class); //insert操作时要填充的字段 @Override public void insertFill(MetaObject metaObject) { LOGGER.info("start insert fill ..."); //根据属性名字设置要填充的值 this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } //update操作时要填充的字段 @Override public void updateFill(MetaObject metaObject) { LOGGER.info("start insert fill ..."); this.setFieldValByName("updateTime",new Date(),metaObject); } } 3、插入数据测试 @RunWith(SpringRunner.class) @SpringBootTest public class CRUDTest { @Autowired private UserMapper userMapper; @Test public void testInsert(){ User user = new User(); user.setName("jack11"); user.setAge(20); user.setEmail("4849111@qq.com"); int result= userMapper.insert(user); System.out.println(result); System.out.println(user); } } 4、修改数据测试 @Test public void testUpdate(){ User user = new User(); user.setId(2L); user.setName("Jackie"); int result = userMapper.updateById(user); System.out.println(result); } 一次插入数据后,create_time和update_time都被填充了设置的时间,做update操作后只有update_time的进行了填充修改。 五、乐观锁插件 乐观锁的核心原理就是提交版本必须等于记录当前版本才能执行更新 意图: 当要更新一条记录的时候,希望这条记录没有被别人更新 乐观锁实现方式: 取出记录时,获取当前version 更新时,带上这个version 执行更新时, set version = newVersion where version = oldVersion 如果version不对,就更新失败 1、添加version到表和类中,为属性添加 ==@Version== 注解 @Version private Integer version; 2、配置插件 @Configuration public class MyBatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor(){ return new OptimisticLockerInterceptor(); } } 3、测试修改的两种情况 @Test public void testOptimisticLocker1() { User user = userMapper.selectById(1128212430124097543L); user.setName("修改后"); int result = userMapper.updateById(user); if (result == 1) { System.out.println("修改成功"); } else { System.out.println("修改失败"); } } @Test public void testOptimisticLocker2() { User user = userMapper.selectById(1128212430124097543L); user.setName("修改后"); user.setVersion(user.getVersion()-1);//测试旧版本 int result = userMapper.updateById(user); if (result == 1) { System.out.println("修改成功"); } else { System.out.println("修改失败"); } } 六、分页插件 启用分页插件和启用乐观锁插件都是通过注册一个Bean完成 1、启用分页插件 @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); } 2、测试分页查询 @Test public void testSelectPage(){ //构建分页条件第二页每页显示3条 Page<User> page=new Page<>(2,3); //使用分页条件查询,不使用其他条件 userMapper.selectPage(page, null); //获取分页后查询出的记录 List<User> records = page.getRecords(); records.forEach(System.out::println); System.out.println("是否有下一页:"+page.hasNext()); System.out.println("是否有上一页:"+page.hasPrevious()); System.out.println("总记录数:"+page.getTotal()); } 七、逻辑删除插件 有些数据希望不再展示,但在物理上仍然存在,这时可以使用逻辑删除。逻辑删除就是在表中添加一个逻辑字段(在上面基础上添加字段 deleted),删除的实质操作就是操作这个逻辑值,在查询、修改时根据此逻辑值进行近一步操作。 1、启用逻辑删除插件 @Bean public LogicSqlInjector logicSqlInjector(){ return new LogicSqlInjector(); } 2、添加逻辑映射配置 mybatis-plus: global-config: db-config: logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) 3、逻辑字段添加注解 ==@TableLogic== @TableLogic private Integer deleted; 4、测试逻辑删除 @Test public void testLogicDelete(){ int result=userMapper.deleteById(1L); System.out.println(result); } 观察日志可以看到生成的sql是update语句 八、性能分析插件 在开发和测试时观察sql执行耗时 1、配置SpringBoot为开发环境 spring: profiles: active: dev 2、启用插件 @Bean @Profile({"dev","test"}) //设置 dev test 环境开启 public PerformanceInterceptor performanceInterceptor(){ return new PerformanceInterceptor(); } 九、CRUD其他操作 @Test public void testSelectById() { User user = userMapper.selectById(1L); System.out.println(user); } @Test public void testSelectBatchIds() { List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); users.forEach(System.out::println); } @Test public void testSelectByMap() { Map<String, Object> param = new HashMap<>(); param.put("name", "jack"); param.put("age", 18); List<User> users = userMapper.selectByMap(param); users.forEach(System.out::println); } @Test public void testSelectMap(){ Page<User> page = new Page<>(2, 3); IPage<Map<String, Object>> mapIPage = userMapper.selectMapsPage(page, null); } @Test public void testDeleteById(){ int result = userMapper.deleteById(1L); System.out.println(result); } @Test public void testDeleteBatchIds(){ int result = userMapper.deleteBatchIds(Arrays.asList(2L,3L,4L)); System.out.println(result); } 欢迎热爱技术的小伙伴和我交流

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。