最新文章

23

2022年01月

后端技术 2022-01-23 0次浏览 0条评论
cs4

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

23

2022年01月

前端技术 2022-01-23 0次浏览 0条评论
cs3

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick Start

23

2022年01月

前端技术 2022-01-23 0次浏览 0条评论
cs3

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

23

2022年01月

前端技术 2022-01-23 0次浏览 0条评论
cs2

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick Start

21

2022年01月

后端技术 2022-01-21 0次浏览 0条评论

标题测试: 这是一级标题这是二级标题这是三级标题这是四级标题这是五级标题这是六级标题字体测试: 这是加粗的文字 这是倾斜的文字 这是斜体加粗的文字 这是加删除线的文字 引用测试: 这是引用的内容 引用内容 引用内容 引用内容 引用内容 分割线测试: 图片测试: 超链接测试: 简书 百度 无序列表测试: 列表内容 子列表内容 子列表内容 子列表内容 列表内容 列表内容 有序列表测试: 列表内容 子列表内容 子列表内容 子列表内容 列表内容 列表内容 表格测试: 头部 头部 头部 内容 内容 内容 内容 内容 内容 内容 内容 内容 参数名 必选 类型 说明 UserContext 是 Authorization 当前登录用户权限为覅违法手动阀手动阀十分 tagId 是 integer(int64) 标签id范文反问我建瓯文件士大夫巫婆二甲方为 123 123 123 123 123 123 123 123 123 123 123 123 单行代码测试: system.out.println("Halloworld") 代码块测试: 1234567891011public abstract strictfp class L2Char extends L2Object { public static final Short ERROR = 0x0001; public void moveTo(int x, int y, int z) { _ai = null; log("Should not be called"); if (1 > 5) { // wtf!? return; } }} MyBatis-Plus概述MyBatis-plus可以节省我们大量的工作时间,所有CRUD代码可以自动化完成! 对于文字排版,我并没有专业的素养。愚之所言,不过个人经验罢了。最后这些经验汇集成了样式集 yue.css。 yue.css 不是一个样式重置(reset),不会影响到其它标签的样式。你所需要的只是给内容区域加上 .yue 的 class。简洁实用,目前用于我的博客,阅乎,当然还有别的朋友在用。 如果你对文字排版有兴趣,可以使用 yue.css。如果你对 yue.css 有意见,也欢迎反馈给我 我时常更新自己的博客程序,也时常更新自己的博客主题。偏右言:「世上有两种前端工程师,写博客的,和写博客的。」我之谓也。我的博客主题一直保持着简洁的样式,保持着我认为适合阅读的文字排版。 JPA、tk-mapper、MyBatis-Plus Space in typography is like time in music. It is infinitely divisible, but a few proportional intervals can be much more useful than a limitless choice of arbitrary quantities. 排版中的空间就想音乐中的时间一样。他是无限整除的,但是按比例的间隔比起毫无限制的使用任意大小要有用很多。 官网:https://mp.baomidou.com/ MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生 特性 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求,以后简单的CRUD操作,不需要自己编写了 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere ) 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(自动帮你生成代码) 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作 快速入门本博客所使用的MyBatis-Plus 版本:3.4.3.1 地址:https://mp.baomidou.com/guide/quick-start.html 使用第三方组件: 导入对应依赖 研究依赖如何配置 代码如何编写 提高技术扩展能力 步骤 创建数据库 mybatis_plus 创建user表 1234567891011121314151617CREATE 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));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');--真实开发中,version(乐观锁)、deleted(逻辑删除)、gmt_create、gmt_modified 编写项目,初始化项目,使用SpringBoot初始化 导入依赖 12345678910111213141516171819202122232425262728293031<dependencies> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.4.3.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency></dependencies> 说明:我们使用mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus,会存在版本差异 连接数据库,与mybatis的相同 123456spring: datasource: username: root password: 123 url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 driver-class-name: com.mysql.cj.jdbc.Driver ==传统方式:pojo-dao(连接mybatis,配置mapper.xml文件)-service-controller== 使用mybatis-plus之后 pojo 1234567891011@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private Long id; private String name; private Integer age; private String email;} mapper接口 12345@Repository// 在对应的Mapper上面继承基本的接口BaseMapperpublic interface UserMapper extends BaseMapper<User> { //所有的CRUD操作都已经编写完成} 注意点:我们需要在主启动类上扫描我们的mapper包下的所有接口@MapperScan("com.czw.mapper") 使用 12345678@Testvoid contextLoads() { //查询全部用户 //参数是一个Wapper,条件构造器,这里我们先不用,使用null List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println);} 思考问题? SQL谁帮我们写的?MyBatis-Plus写好了 方法哪里来的?MyBatis-Plus写好了 配置日志我们所有的sql现在是不可见的,我们希望知道他是怎么执行的,所以我们必须看日志! 123mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 配置完日志后,需要注意自动生成的SQL CRUD扩展插入操作 insert插入 1234567891011@Testpublic void testInsert() { User user = new User(); user.setName("Mootens"); user.setAge(3); user.setEmail("2238782426@qq.com"); int res = userMapper.insert(user); // 帮我们自动生成id System.out.println(res); // 受影响行数 System.out.println(user); // 发现id会自动回填} 数据库插入的id为:全局的唯一id 主键生成策略 默认 ASSIGN_ID 全局唯一id 分布式系统唯一id生成方案:https://www.cnblogs.com/haoxinyue/p/5208136.html 雪花算法: snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一! 主键自增 我们需要配置主键自增: 实体类字段上 @TableId(type = IdType.AUTO) 数据库字段一定要是自增! 其余的源码解释 1234567public enum IdType { AUTO(0), // 数据库id自增 NONE(1), // 未设置主键 INPUT(2), // 手动输入,设置手动输入id后需要自己配置id ASSIGN_ID(3), // 默认的全局唯一id ASSIGN_UUID(4); // 全局唯一id uuid} 更新操作12345678910111213// 测试更新@Testpublic void testUpdate() { User user = new User(); // 通过条件自动拼接动态SQL user.setId(6L); user.setName("Moot"); user.setAge(21); // 注意:updateById 但是参数是一个对象! int res = userMapper.updateById(user); System.out.println(res);} 所以的SQL都是自动帮你动态配置的! 自动填充创建时间、修改时间,这些操作一遍都是自动化完成的,我们不希望手动更新! 阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified,几乎所有的表都要配置上!而且需要自动化! 方式一:数据库级别(工作中不允许修改数据库) 在表中新增字段 create_time、update_time 更新实体类,再次测试插入方法 12private Date createTime;private Date updateTime; 方式二:代码级别 删除数据库的默认值、更新操作 实体类字段上需要增加注解 123456//字段添加填充内容@TableField(fill = FieldFill.INSERT)private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime; 编写处理器来处理这个注解即可! 1234567891011121314151617@Slf4j@Component // 不要忘记把处理器加入到IOC容器中public class MyMetaObjectHandler implements MetaObjectHandler { // 插入时的填充策略 @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill......"); this.setFieldValByName("creatTime", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } // 更新时的填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("start update fill......"); this.setFieldValByName("updateTime", new Date(), metaObject); }} 测试插入、更新,观察结果 乐观锁 乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不去上锁,如果出现问题再次更新值测试 悲观锁:顾名思义十分悲观,他总是认为总是出现问题,无论干什么都会上锁!再去操作! 这里主要讲解乐观锁机制 乐观锁实现方式: 取出记录时,获取当前version 更新时,带上这个version 执行更新时, set version = newVersion where version = oldVersion 如果version不对,就更新失败 123456789乐观锁:1、先查询,获得版本号 version = 1-- Aupdate user set name = "mootens", version = version +1 where id = 2 and version = 1-- B 线程抢先完成,这个时候 version = 2,会导致 A 修改失败!update user set name = "mootens", version = version +1 where id = 2 and version = 1 测试一下乐观锁插件 给数据库中增加 version 字段 实体类加对应的字段 12@Version // 乐观锁 version 注解private Integer version; 注册组件 1234567891011121314@MapperScan("com.czw.mapper") // 扫描我们的mapper文件夹@EnableTransactionManagement@Configuration // 配置类public class MyBatisPlusConfig { // 注册乐观锁插件 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }} 测试 1234567891011121314151617181920212223242526272829// 测试乐观锁成功!@Testpublic void testLocker() { // 查询用户信息 User user = userMapper.selectById(1L); // 修改用户信息 user.setName("Moot"); user.setEmail("2238782426@qq.com"); // 执行更新操作 userMapper.updateById(user);}// 测试乐观锁失败!@Testpublic void testLocker2() { // 查询用户信息 User user = userMapper.selectById(1L); // 模拟另外一个线程进行插队操作 User user2 = userMapper.selectById(1L); user2.setName("Moot222"); // 修改用户信息 user.setName("Moot111"); // 自旋锁来多次尝试提交 userMapper.updateById(user); //如果没有乐观锁就会覆盖插队线程的值} 查询操作123456789101112131415161718192021222324// 测试查询@Testpublic void testSelectById() { User user = userMapper.selectById(1L); System.out.println(user);}// 测试批量查询@Testpublic void testSelectBatchIds() { List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); users.forEach(System.out::println);}// 条件查询 map@Testpublic void testSelectByMap() { HashMap<String, Object> map = new HashMap<>(); // 自定义要查询内容 map.put("name", "Mootens"); map.put("age", 3); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println);} 分页查询分页在网站使用的十分之多! 原始的 limit 进行分页 pageHelper 第三方插件 MP其实内置了分页插件 如何使用 配置拦截器组件即可 12// 分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); 直接使用Page对象即可 12345678910// 测试分页查询@Testpublic void testPage() { // 参数一:当前页 // 参数二:页面大小 Page<User> page = new Page<>(2, 5); userMapper.selectPage(page, null); page.getRecords().forEach(System.out::println);} 删除操作基本的删除操作 12345678910111213141516171819// 测试删除@Testpublic void testDeleteById() { userMapper.deleteById(1428601086570217475L);}// 通过id批量删除@Testpublic void testDeleteBatchId() { userMapper.deleteBatchIds(Arrays.asList(1428563684476678145L, 1428601086570217473L));}//通过map删除@Testpublic void testDeleteMap() { HashMap<String, Object> map = new HashMap<>(); map.put("name", "Mootens"); userMapper.deleteByMap(map);} 逻辑删除 物理删除:从数据库中直接移除 逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效!deleted = 0 => deleted = 1 管理员可以查看删除的记录!防止数据的丢失,类似于回收站! 测试: 在数据表中增加一个 deleted 字段 实体类增加属性 12@TableLogic // 逻辑删除private Integer deleted; 配置 123456mybatis-plus: global-config: db-config: logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) 测试删除 通过结果可以观察到,执行 DeleteById 操作后,走的是更新操作,并不是删除操作,记录依然在数据库中,deleted 值发生变化 执行查询操作,MP会帮我们自动拼接 sql ,会自动过滤被逻辑删除的字段 性能分析插件我们在平时的开发中,会遇到一些慢 sql,可以通过测试、druid…… 在新版MP中,我们使用 p6spy 组件来进行性能分析 作用:执行 SQL 分析打印(性能分析拦截器),用于输出每条 SQL 语句及其执行时间 导入依赖 123456<!-- https://mvnrepository.com/artifact/p6spy/p6spy --><dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version></dependency> 配置yml 123456spring: datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver username: root password: 123 url: jdbc:p6spy:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 配置spy.properties 123456789101112131415161718192021222324#3.2.1以上使用modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory#3.2.1以下使用或者不配置#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory# 自定义日志打印logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger#日志输出到控制台appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger# 使用日志系统记录 sql#appender=com.p6spy.engine.spy.appender.Slf4JLogger# 设置 p6spy driver 代理deregisterdrivers=true# 取消JDBC URL前缀useprefix=true# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.excludecategories=info,debug,result,commit,resultset# 日期格式dateformat=yyyy-MM-dd HH:mm:ss# 实际驱动可多个#driverlist=org.h2.Driver# 是否开启慢SQL记录outagedetection=true# 慢SQL记录标准 2 秒outagedetectioninterval=2 测试 测试结果会显示当前所执行的 sql 语句,执行的时间 条件构造器十分重要:wrapper 我们写的一些复杂的 sql 可以用他来替代 测试一 12345678910@Testvoid contextLoads() { // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于大于12 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .isNotNull("name") .isNotNull("email") .ge("age", 12); userMapper.selectList(wrapper).forEach(System.out::println);} 测试二 1234567@Testpublic void test2() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name", "Moot"); User user = userMapper.selectOne(wrapper); System.out.println(user);} 测试三 12345678// 查询年龄为20~30之间的用户@Testpublic void test3() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age", 20, 30); // 区间 Integer count = userMapper.selectCount(wrapper);// 查询结果数 System.out.println(count);} 测试四 1234567891011// 模糊查询@Testpublic void test4() { QueryWrapper<User> wrapper = new QueryWrapper<>(); // 左和右分别代表着%在左边还是右边 t% wrapper .notLike("name", "M") .likeRight("email", "t"); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println);} 测试五 12345678910// id在子查询中查出来@Testpublic void test5() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.inSql("id", "select id from user where id<3"); // AND (id IN (select id from user where id<3)) List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println);} 测试六 12345678// order排序 通过id进行排序@Testpublic void test6() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println);} 代码生成器AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率 导入依赖 这里的 swagger 我选用的是 knife4j 123456789101112131415161718<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version></dependency><dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version></dependency><!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-spring-boot-starter --><dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version></dependency> 配置类 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566public class GeneratorCode { public static void main(String[] args) { // 需要构建一个 代码自动生成器 对象 AutoGenerator generator = new AutoGenerator(); // 配置策略 // 1.全局配置 GlobalConfig config = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); config.setOutputDir(projectPath + "/src/main/java"); config.setAuthor("Mootens"); config.setOpen(false); config.setFileOverride(false); // 是否覆盖 config.setServiceName("%sService"); // 去Service的I前缀 config.setIdType(IdType.ASSIGN_ID); config.setDateType(DateType.ONLY_DATE); config.setSwagger2(true); generator.setGlobalConfig(config); // 2.设置数据源 DataSourceConfig sourceConfig = new DataSourceConfig(); sourceConfig.setUrl("jdbc:mysql://localhost:3306/"); sourceConfig.setDriverName("com.mysql.cj.jdbc.Driver"); sourceConfig.setUsername("username"); sourceConfig.setPassword("password"); sourceConfig.setDbType(DbType.MYSQL); generator.setDataSource(sourceConfig); // 3.包的配置 PackageConfig packageConfig = new PackageConfig(); packageConfig.setModuleName("user"); packageConfig.setParent("com.czw"); packageConfig.setEntity("entity"); packageConfig.setMapper("mapper"); packageConfig.setService("service"); packageConfig.setController("controller"); generator.setPackageInfo(packageConfig); // 4.策略配置 StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setInclude("user"); // 设置要映射的表名 他是一个 String... 可以设置多个 strategyConfig.setNaming(NamingStrategy.underline_to_camel); strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); strategyConfig.setEntityLombokModel(true); // 自动lombok strategyConfig.setLogicDeleteFieldName("deleted"); //设置逻辑删除字段 // 自动填充配置 TableFill createTime = new TableFill("create_time", FieldFill.INSERT); TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(createTime); tableFills.add(updateTime); strategyConfig.setTableFillList(tableFills); // 乐观锁 strategyConfig.setVersionFieldName("version"); strategyConfig.setRestControllerStyle(true); // 开启Restful驼峰命名格式 strategyConfig.setControllerMappingHyphenStyle(true); // localhost:8080/hello_id_2 generator.setStrategy(strategyConfig); generator.execute(); // 执行 }} 测试结果(官网效果) 第九个9.19.2第十个10.110.2