Java Study!!
type: channel
link: https://www.kaijavademo.top/
description: The path of a programmer.
items: 记录一次粗心的部署问题
items: SpringBoot3笔记：数据访问|基础特性|场景整合
items: SpringBoot3笔记：快速搭建|核心流程总结|Web开发
items: Redis笔记：管道|发布订阅|复制|哨兵|集群|SpringBoot集成Redis
items: Redis笔记：十大数据类型指令|Redis键|持久化|事务
items: 算法题：数据结构篇(持续更新)
items: SSM整合笔记：@Bean|整合
items: 算法题：基础算法篇
items: MyBatis笔记：各种查询方式|特殊SQL|自定义映射|多表联查|动态SQL|逆向工程
items: MyBatis笔记：搭建框架|核心文件详解|模板|封装SqlSession|获取参数值

记录一次粗心的部署问题
type: item
date: 2024-05-13T18:44:00+00:00
description: 😎自己也是好久没有更新了，先说下这段时间都在干嘛？学校里功课有点多，忙着做实验写报告；平时自己也要去继续自己的方向的学习，几乎没什么时间；每天都安排的满满的，在图书馆和教室来回奔波，累~~，晚上还要回到宿舍抽出一些仅有的时间来学习算法；有的时候还要网上冲浪，和云服务器的群友们play。自己像打算再部署一个非常长期的halo博客用于记录我的生活，和写写平日里生活中的笔记的想法了，但是太忙了，一直拖着。 这是自己的安排，发现会安排事的人一直都不会闲下来(自己在假期做了和一个小伙伴做了一个项目，前段时间好不容易抽出时间有空结束了整个项目。5.2的时候自己得意洋洋的开始部署项目，但是到5.4自己都没部署好，然后一整个五一也是这样(,人麻了，最后在5.10的时候还是要感谢一位前端的群友帮助下，成功部署上去了。首先来说说自己为什么没有部署上去(愚蠢，大佬请忽略，老实说，我忘的差不多了😭~~）首先自己第一次用1panel，运维面板之前只用过宝塔，为什么改用？是因为前段时间专业版一折券，和云服务器的群友们一起买的。通俗来说，是一个像宝塔一样的面板，只不过自己真的不太熟悉怎么用新面板。好多位置上的东西都不太一样。自己和平时的群友聊天的时候，自己试着摸索了下CF(cloudflare)，生成了个证书，然后结构变得就很复杂，报错的话自己也是没什么头绪然后部署项目的时候，刚开始起始开发环境的时候(一开始的时候实际上非常接近线上了已经)，我已经准备了多环境配置，但是还是报错。一度让我从https先切换为http，为什么要切换，因为用https需要生成证书什么的，非常麻烦（实际上也不麻烦，后面也是生成了，并且是端对端的完全加密）主要是想看清为啥报错。然后自己改http的时候修改了好多的配置，改成http最后部署项目上去的时候。我的项目是首次访问，不管是访问哪个页面，都会重定向到登录页，登陆后在切换回来的这样一个逻辑。结果发现无法重定向，原来是需要在nginx/openresty(1p中的）中中配置一下。然后发现http不安全，不保存session到浏览器中，很奇怪。然后我就从http>>https又花费了好久。最主要的是在解决一系列问题后，发现出现了反复重定向到登录页的问题，由于项目中封装了一个myAxios.ts的这样一个请求响应封装类，找不到报错。最后在请求中右键复制为cURL才一度发现问题。问题在于原来是自己在配置线上application-prod.yml的时候，复制的时候在种cookie的时候，没有配置对应的域名/ip。导致了session一度种在了本地，因为没有配置，所以默认是localhost，唉，血泪的教训。下次记住了。、这里是开发的配置application.yml 但是线上的删除了，其实也是localhost还是来稍微总结一下把，打开线上部署要注意一些细节。🌟前端的baseUrl直接指向配置的域名，不要跟端口号；后端的application-prod.yml中要配置好cookie的地址，并且如果用了redis要配置好redis的配置（和本地可能不太一样）MySQL也是如此~~；服务器中的 [label color="green"]80和443[/label] 端口要留给 [label color="green"]nginx/openresty[/label] ,不要乱占用。如果用https，后端也千万不可直接监听443端口（我糊涂了呜呜）；有CF证书的话，就是完全加密；后端跨域在调试问题的时候可以全开，后面有需要可以再具体调整；面板中创建网站文件的时候，执行后端文件的时候，最好是先单独用java -jar执行下，没有报错的话。然后使用nohup开启守护线程执行项目。🤑好了就先说这么多把，好久没有写过文章了，这会有些迟了，先休息了，再见啦。我一直在，请不要忘记我，让我们一起加油！

SpringBoot3笔记：数据访问|基础特性|场景整合
type: item
date: 2023-10-25T09:24:00+00:00
description: 在有了Web开发的前提下，如果对数据访问场景进行开发，那么就是一个非常完整的业务流程了。通过SpringBoot整合SSM使用 [label color="green"]Spring Initializr[/label] 创建向导快速构建boot工程。以后使用这样的方式。在向导中勾选对应需要的启动器，比如新版的数据库驱动、mybatis场景、web场景...SpringBoot整合MyBatis在 [label color="green"]application.properties[/label] 中配置对应的基本数据源参数#1.三大框架整合，最先配置数据源信息 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.type=com.zaxxer.hikari.HikariDataSource #配置对应的数据库信息 spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username= spring.datasource.password=继续在此文件中，配置整合MyBatis。在启动类的子包创建Bean包，添加对应Bean类和@Data注解，根据对应的数据库，来封装表的数据。在启动类的子包创建Mapper包，添加一个接口，用来查表数据的，与此还需要有一个XML文件与之对应。在resources(类路径)创建一个mapper包，用来存放XML文件。单机UserMapper接口类使用插件快速创建XML文件。安装MyBatisX插件，帮我们生成Mapper接口的XML文件即可。我们在Mapper接口中书写方法，使用 [label color="red"]alt + enter[/label] 快速识别生成XML标签，我们需要手动在标签体书写对应的SQL语句。为了方便传参(#{})，我们需要在对应的接口形参位置添加 [label color="pink"]@Param("")[/label] 注解。UserMapper接口/** * @author: TangZhiKai * @create: 2023-10-25 15:38 * @description: 接口，用来查t_user表数据的，还需要有个xml文件与之对应 **/ public interface UserMapper { /** * 1.每个方法都在Mapper文件中有一个SQL标签对应， * 2.所有参数都应该用@Param进行签名，以后使用指定的名字在SQL中取值 * @param id * @return */ //使用插件给形参添加@Param注解，以后XML文件中就用 #{}来取,这是一个良好的习惯 public TUser getUserById(@Param("id") Long id); }启动类中添加注解@MapperScan/** * 1.使用@MapperScan 告诉MyBatis，扫描哪个包下面的所有接口 * 2.通过properties文件配置配置项，使用mybatis.mapper-locations告诉MyBatis，每个接口的XML文件都在哪里。 * 3.MyBatis自动关联绑定 */ @MapperScan(basePackages = "com.atguigu.boot3.ssm.mapper") @SpringBootApplication public class Boot305SsmApplication { public static void main(String[] args) { SpringApplication.run(Boot305SsmApplication.class, args); } }修改配置文件配置文件properties中需要指定，扫描类路径下对应的包文件 mybatis.mapper-locations=classpath:/mapper/*.xml 那么实际上就会从当前包中寻找XML文件，然后对应再文件的命名空间，在找到实际的Mapper接口。实现接口和XML文件的关联。打开对应的驼峰命名规则，这样可以不用在SQL中起别名。# 2.配置整合MyBatis #指定接口的XML文件为类路径下mapper中的所有文件 mybatis.mapper-locations=classpath:/mapper/*.xml #开启驼峰命名规则 mybatis.configuration.map-underscore-to-camel-case=true写Controller、(Service）测试/** …

SpringBoot3笔记：快速搭建|核心流程总结|Web开发
type: item
date: 2023-10-01T08:19:00+00:00
description: 基于JDK17 + SpringBoot3的入门笔记，快速搭建 + 常用注解[video title="快速搭建 " url="https://img.kaijavademo.top/typecho/uploads/2023/10/videos/%E5%BF%AB%E9%80%9F%E6%90%AD%E5%BB%BA.mkv " container="beu1prgsiin" subtitle=" " poster=" "] [/video]常用注解[video title="常用注解 " url="https://img.kaijavademo.top/typecho/uploads/2023/10/videos/%E5%B8%B8%E7%94%A8%E6%B3%A8%E8%A7%A3.mkv " container="baqik9157t" subtitle=" " poster=" "] [/video]/** * @author: TangZhiKai * @create: 2023-09-30 17:24 * @description: 配置类 **/ @Import(FastsqlException.class) //导入第三方类进入IoC容器，给容器中放指定类型的组件，组件的名字默认是全类名 @SpringBootConfiguration //这是一个配置类，替代以前的配置文件.配置类本身也是容器中的组件 //@Configuration //这是一个配置类，替代以前的配置文件.配置类本身也是容器中的组件 public class AppConfig { /** * 1.组件默认是单实例的 * @return */ @Scope("prototype") //可以修改为多实例 @Bean("userHaha") //替代以前的Bean标签.组件在容器中的名字默认是方法名 //可以直接修改注解的值在Bean标签中 public User user(){ User user = new User(); user.setId(1L); user.setName("张三"); return user; } /*@Bean public FastsqlException fastsqlException(){ return new FastsqlException(); }*/ } 条件注解如果注解指定的条件成立，则触发指定行为@ConditionalOnClass：如果类路径中存在这个类，则触发指定行为@ConditionalOnMissingClass：如果类路径中不存在这个类，则触发指定行为@ConditionalOnBean：如果容器中存在这个Bean(组件)，则触发指定行为@ConditionalOnMissingBean：如果容器中不存在这个Bean(组件)，则触发指定行为....//@ConditionalOnClass可以直接标到类上，当系统中存在这个类，以下所有东西全部生效 //放在类级别，如果注解判断生效，则整个配置类才生效 @ConditionalOnClass(name = "com.alibaba.druid.FastsqlException") @SpringBootConfiguration public class AppConfig2 { //当类路径中存在这个FastsqlException类，则生效 //放在方法级别，只是单独对这个方法进行注解判断 @ConditionalOnClass(name = "com.alibaba.druid.FastsqlException") @Bean public Cat cat01() { return new Cat(); } @ConditionalOnMissingClass(value = "com.alibaba.druid.FastsqlException") @Bean public Dog dog01() { return new Dog(); } //当容器中有dog的对象，就向容器中添加一个张三 @ConditionalOnBean(value = Dog.class) @Bean public User zhangsan() { return new User(); } //当容器中没有dog对象，就向容器中添加一个李四 @ConditionalOnMissingBean(value = Dog.class) @Bean public User lisi() { return new User(); } }属性绑定将容器中任意组件(Bean)的属性值和配置文件的配置项进行绑定@ConfigurationProperties…

Redis笔记：管道|发布订阅|复制|哨兵|集群|SpringBoot集成Redis
type: item
date: 2023-09-20T07:02:00+00:00
description: 基于Redis7的学习笔记，包含有Redis管道，目前仍在持续更新。Redis管道Redis管道和Redis事务截然不同的两个东西。可以类比为Java和JavaScript之间的关系。把所有命令排成一行，像流水线一样去处理。管道(pipeline)可以一次性发送多条命令给服务端，服务端依次处理完完毕后， [label color="red"]通过一条响应一次性将结果返回，通过减少客户端与redis的通信次数来实现降低往返延时时间[/label] 。pipeline实现的原理是队列，先进先出特性就保证数据的顺序性。我们之前学过的批量添加无法一次性添加多种数据类型，这个也是我们出现的问题，用管道来解决。我们可以将一次性添加的命令放在myredis/下的一个新的txt文件。在服务器外部执行语句 cat cmd.txt | redis-cli -a abc123 --pipe服务器内部可以使用，如图。[note type="primary flat"]管道小总结：Ⅰ.Pipeline与原生批量命令对比①原生的批量命令是原子性，pipeline是非原子性。②pipeline支持批量执行不同的命令。③原生批命令是服务器实现，而pipeline需要服务端与客户端共同完成。Ⅱ.Pipeline与事务对比①事务具有原子性，管道不具有原子性②事务一条一发，只有在接收到exec命令后才会执行。而管道是一次性将多条命令发送到服务器。③执行事务会阻塞其他命令执行，而执行管道中的命令不会。Ⅲ.管道自身注意事项①管道的缓冲指令只是会依次执行，不保证原子性，如果执行中指令发生异常，将会继续执行后续的指令②使用pipeline组装的命令个数不能太多，不然数据量大客户端阻塞的时间可能过久，同时服务端此时也被迫回复一个队列答复，占用很多内存。[/note]官方解释So if you need to send a lot of commands with pipelining, it is better to send them as batches each containing a reasonable number, for instance 10k commands, read the replies, and then send another 10k commands again, and so forth.发布订阅类似于MQ消息中间件，了解即可，我们还是需要将专业的事情交给专业的人去干。pub|sub作为第一代消息中间件，stream作为第二代消息中间件(就是说可以理解为MQ的前身雏形)是一种消息通信模式，发送者发送消息，订阅者接收消息，可以实现进程见的消息传递。Redis可以实现消息中间件的功能，即通过发布订阅实现消息的引导和分流。专业的事情交给专业的中间件处理，redis做好分布式缓存的功能就够了。开启三个客户端，演示客户端A、B订阅消息，客户端C发布消息演示批量订阅和发布取消订阅[note type="warning flat"]存在的问题(缺点)：①没有持久化，必须先订阅再发布。如果先发布消息没有订阅者，消息直接丢弃。②没有ACK机制，无法保证消息的消费成功③以上缺点导致该模式在生产结构中无用武之地，因此Redis5.0新增了Stream。[/note]Redis复制Redis复制非常重要，生产工作中，redis不可能是单机，后面的哨兵和集群都要建立在这个基础之上。主机(master)以写为主，从机(slave)以读为主。当主机(master)数据发生变化的时候，自动将新的数据异步同步到其他slave数据库。能干嘛？读写分离容灾恢复，对之前持久化的一种更好的补充数据备份水平扩容支撑高并发使用：配从库不配主库。主机配置了requirepass参数，需要密码登录；从机要配置masterauth来设置校验密码，否则主机就会拒绝主机的访问请求。基本操作命令[note type="info flat"]主从复制:replicaof 主库IP 主库端口;配从库不配主库改换门庭:slave of 新主库IP 新主库端口自立为王:slaveof no one[/note]案例需要使用三个虚拟机来模拟这个操作，配置，我用视频的形式来解释我只使用一个虚拟机来模拟三个不同的虚拟机的场景。实际上就是开三个进程，使用不同的配置文件，配置文件中配置好对应的信息和端口号什么的就可以。[video title="主从复制配置准备 " url="https://img.kaijavademo.top/typecho/uploads/2023/09/videos/%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6%E5%87%86%E5%A4%87.mkv …

Redis笔记：十大数据类型指令|Redis键|持久化|事务
type: item
date: 2023-09-10T09:12:00+00:00
description: Redis是一种键值对类型的缓存数据库，在Linux下使用，基于Redis7的入门笔记,写的很入门,已完结。包括Redis键，value中所对应的所有数据结构，以及持化双雄AOF + RDB，还有Redis的事务。初始化启动redis-a 密码 -a后面跟在redis.config中设置的密码 -p默认不写端口号就是6379.redis-server /myredis/redis7.conf redis-cli -a abc123关闭如果是在服务器内部直接使用 SHUTDOWN 关闭即可如果是在远程可以使用单实例关闭redis-cli -a abc123 shutdown退出 quit十大数据类型这里我们所说的数据类型是指的是value的数据类型，key的数据类型都是字符串。命令不区分大小写，key是区分大小写的。StringRedis中用的最频繁的，最重要，绝绝对对的一号男神。String类型是 [label color="red"]二进制安全[/label] 的，支持序列化。相当于String可以包含任何数据，一个key对应一个value，是最重要最基本的数据类型，一个redis中字符串value最多是512M。set key value [...] # 这里面有五个关键参数，留意keepttl get key # 同时设置/获取多个值 mset key value [key value...] mget key [key...] getrange key 0 -1# 获取指定区间范围内的值(0 -1 表示获取所有值，以后不再赘述) setrange key index abc # 将abc开始赋值给key对应value的下标index incr key # 递增数字，等同于++ incrby key number #为key的value值递增指定的数字number decr key # 递减数字，等同于-- strlen key # 获取key的对应的值的长度 append key value # 将value追加到key对应值之后 setnx key value # 当key不存在的时候创建 setex key secounds value # 创建key的时候设置过期时间secounds getset # 等同于set key value GET [label color="blue"]应用场景：抖音的无限点赞某个视频[/label]List(列表)类比Java，Redis列表是简单的字符串列表，可以添加元素到 [label color="blue"]头部[/label] 或者 [label color="blue"]尾部[/label] ，底层是一个双端链表。lpush/rpush list(key) 1 2 3 4 # 从左边添加数据/从右边添加数据，注意从左边添加数据的时候数据会像进栈一样，先进入的在右边 lrange list(key) 0 -1 #从左侧遍历 lpop/rpop list(key) #从左端点弹出数据/从右端点弹出数据 lindex list(key) 0 # 按照索引下标获得元素 llen list(key) # 返回当前list集合的长度，相当于Java中的size lrem list(key) N v1 # 删除N个值等于v1的元素 ltrim list startIndex endIndex # 删除起始到终止索引的元素 rpoplpush list1 list2 # 将list1的第一个元素(尾部)弹出，压入list2中。 lset list(key) index value # 将某个集合(key)的index下标对应的元素替换为value值 linsert list(key) before/after value1 value2 # 在某个集合value1的前面/后面插入value2[label color="blue"]应用场景：微信号订阅的消息[/label]Hash哈希表类比Java Map<String,Map<Object,Object>> [note type="default flat"]以前 key1 value1现在 key1 field value1[/note]hset key field value [field value...] # 添加一个数据 hget key field # 查询某个数据 hmset key field value... # 一次性添加多个数据 hmget key field field ..# 一次性查询某个key的多个数据 hgetall key # 遍历key的值所有数据，包括field 和 value hdel key …

算法题：数据结构篇(持续更新)
type: item
date: 2023-09-09T16:55:00+00:00
description: 这是我用于分享和复习算法笔记的地方，此篇主要是围绕关于数据结构的一些算法题。我在AcWing上刷题，可以和我一起:)AcWing 826. 单链表原址解题过程import java.io.*; import java.util.*; public class Main { private static final int N = 100010; private static final int[] e = new int[N]; private static final int[] ne = new int[N]; private static int idx; private static int head; public static void main(String[] args) throws IOException{ init(); //单链表 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] arrStr = br.readLine().split(" "); int n = Integer.parseInt(arrStr[0]); while(n -- > 0) { arrStr = br.readLine().split(" "); String let = arrStr[0]; if("H".equals(let)) { int x = Integer.parseInt(arrStr[1]); addToHead(x); } if("D".equals(let)) { int k = Integer.parseInt(arrStr[1]); if(k == 0) head = ne[head]; else remove(k - 1); } if("I".equals(let)) { int k = Integer.parseInt(arrStr[1]); int x = Integer.parseInt(arrStr[2]); add(k - 1, x); } } for(int i = head ;i != -1;i= ne[i]) System.out.print(e[i] + " "); br.close(); } private static void init() { //初始化的方法 idx = 0; head = -1; } //将x插入到头结点 private static void addToHead(int x ) { e[idx] = x; ne[idx] = head; head = idx ++; } //将x插入到下标为k的结点后,尾插法 private static void add(int k,int x) { e[idx] = x; ne[idx] = ne[k]; ne[k] = idx ++; } //删除下标为k后面的结点删除 private static void remove(int k) { ne[k] = ne[ne[k]]; } } 附上我的视频，(●'◡'●)[video title="单链表 " url="https://img.kaijavademo.top/typecho/uploads/2023/09/videos/%E5%8D%95%E9%93%BE%E8%A1%A8.mkv " container="b8fo4ln3enn" subtitle=" " poster=" "] [/video]AcWing 829. 模拟队列原址解题过程import java.io.*; import java.util.*; public class Main { private static final int N = 100010; private static final int[] q = new int[N]; private static int hh = 0;//队头 private static int tt = -1;//队尾 //往队尾插入元素，队头弹出元素 public static void main(String[] args) throws IOException{ //829. 模拟队列 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] arrStr = br.readLine().split(" "); int m = Integer.parseInt(arrStr[0]); while(m-- > 0)…

SSM整合笔记：@Bean|整合
type: item
date: 2023-08-28T18:36:00+00:00
description: 在单独学习完SSM的三个部分后，需要对于三个部分做一个整合，整合过程中我发现了自己很多的不足。一方面我更新了之前的内容，由于之前的内容太长，不方便在原有基础上再添加一些补充一些自己的遗漏，特写一篇文章，发现整合过程中的不足。看上去比较简短，博主想了好久最终还是决定发出来了。然后现在要开学了，博主目前也在学习springboot3和算法，平时将一些有意义的算法题在对应博客继续更新，由于springboot3的版本太新问题有点多(已经踩了不少坑了，心痛)，暂时不做boot的笔记，往后会学习redis，后面的更新会比较慢,等到假期就会恢复回来，敬请期待~@Bean注解谈到这个注解，一定和配置类有关系，顺便熟悉一下配置类pom文件中依赖还是导一下，需要使用对应的spring组件 <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.6</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.3.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.14</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.2.4.RELEASE</version> </dependency> </dependencies>我们先创建配置类代替xml配置文件[video title="配置类 " url="https://img.kaijavademo.top/typecho/uploads/2023/08/videos/2023-08-26%2019-32-21.mkv " container="b3unv1gktx" subtitle=" " poster=" "] [/video]然后我们使用@Bean标签[video title="@Bean详解 " url="https://img.kaijavademo.top/typecho/uploads/2023/08/videos/2023-08-26%2021-02-00.mkv " container="btgzcxwab3t" subtitle=" " poster=" "] [/video]配置类源码JavaConfigurationpackage com.atguigu.config; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.*; import org.springframework.jdbc.core.JdbcTemplate; import javax.sql.DataSource; import javax.xml.crypto.Data; /** * @author: TangZhiKai * @create: 2023-08-26 19:05 * @description: Java的配置类，替代xml配置文件 * 配置类中可以做三件事 * 1.包扫描注解配置 * 2.引用外部配置文件 * 3.声明第三方依赖的bean组件 * * 步骤一：添加@Configuration 代表我们是配置类 * 步骤二：实现上面的三个功能注解 **/ @ComponentScan(value = "com.atguigu.ioc_01") @PropertySource(value = "classpath: jdbc.properties") @Configuration public class JavaConf…

算法题：基础算法篇
type: item
date: 2023-08-23T13:12:00+00:00
description: 这里是我刷算法题的笔记，来自于acwing，我会将我做算法题的心得和改进写在笔记中用于分享和交流，你也可以和我一起刷一起交流。AcWing 786. 第k个数题目一开始我想到的是，用快排，然后遍历做一个判断(看起来笨笨的，新手只能想到这个QAQ)import java.util.Scanner; import java.util.stream.IntStream; public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int k = sc.nextInt(); int[] arr = IntStream.range(0,n).map(i -> sc.nextInt()).toArray(); int[] sortedArr = quickSort(arr,0,n-1); for(int i = 0; i < sortedArr.length; i++){ if(i == (k-1)){ System.out.println(sortedArr[i]); } } } private static int[] quickSort(int[] q,int l,int r){ if(l >= r) return q; int p = q[l+ r >>1],i = l-1,j = r+1; while(i<j){ while(q[++i] < p); while(q[--j] > p); if(i<j){ int t = q[i]; q[i] = q[j]; q[j] = t; } } quickSort(q,l,j); quickSort(q,j+1,r); return q; } }这样写勉勉强强过了，但是这样处理笨笨的，我在看了y总的题解之后知道这道题有一种更好的办法。实际上我们先回顾快排的步骤确定分解点（一共有四种，听说现在使用中点和随机不会超时）处理分界点左边和右边的数，保证左边的最大值小于右边的最小值，当然分界点的元素相同的话分布在左右两边递归处理左边和右边的数快排的时间复杂度平均是 [label color="blue"]O(nlogn)[/label] ,这道题其实上使用的是快速选择排序。我们可以这样考虑，找第k个数，无非是在处理完分解点之后，将一组数分成两部分，我们可以比较第k个数的位置，那么此会产生两种情况当左区间的个数sl大于k，说明k在左边当左区间的个数sl小于k，说明k在右边。此时k在右边的位置应该是k-sl。通过这两种思路我们只需要把k作为形参放在快排方法中，方法的第三步我们稍作调整，递归中加入k，根据不同的情况返回不同的递归值。如果遍历有右区间，那么应该寻找的是第k - sl个数。 [label color="red"]会保证第k小的数一直在递归的区间中，那么当区间里只有一个数的时候，就一定是要找的数了。[/label] 那么我们需要调整返回值，和一开始的出口返回数组的左边界（q[l]）即可。import java.util.Scanner; import java.util.stream.IntStream; public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int k = sc.nextInt(); int[] arr = IntStream.range(0,n).map(i -> sc.nextInt()).toArray(); System.out.println(quickSort(arr,0,n-1,k)); } private static int quickSort(int[] q,int l,int r, int k){ if(l >= r) return q[l]; int p = q[l+ r >>1],i = l-1,j = r+1; while(i<j){ while(q[++i] < p); while(q[--j] > p); if(i<j){ int t = q[i]; q[i] = q[j]; q[j] = t; } } //计算左区间的长度 int sl = j - l + 1; //始终保持k在递归区间，当区间只有一个数的时候,那么就是我们要找的数 if(sl >= k){ return quickSort(q,l,j,k); }else{ return quickSort(q,j+1,r,k-sl); }…

MyBatis笔记：各种查询方式|特殊SQL|自定义映射|多表联查|动态SQL|逆向工程
type: item
date: 2023-08-22T19:20:00+00:00
description: MyBatis笔记：各种查询功能,特殊SQL语句，动态SQL语句，MyBatis中的一二级缓存，清新版和奢华版的逆向工程的使用，包括我自己的对于多表联查的视频解释，分页查询的插件，还有我对于报错的问题解决思路。篇幅较长，可以选择所需查看。各种查询方式查询单条信息/多条信息SelectMapper接口public interface SelectMapper { /* 根据id来查询用户信息/返回值可以使用User或者是List<User> */ public abstract List<User> getUserById(@Param("id") Integer id); /* 查询所有的用户信息 */ public abstract List<User> getAllUser(); }SelectMapper.xml select * from t_user where id = #{id} <!--List<User> getAllUser();--> select * from t_user 测试类public class SelectMapperTest { /** * MyBatis的各种查询功能 * 1.若查询出的数据只有一条， * a>可以通过实体类对象 * b>可以通过List集合接收 * c>可以通过Map集合接收 * * 2.若查询出的数据有多条， * a>可以通过List集合接收 * b>可以通过Map集合接收 *注意：一定不能通过实体类对象来接收，此时会抛异常TooManyResultsException * */ @Test public void testGetAllUser(){ SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SelectMapper mapper = sqlSession.getMapper(SelectMapper.class); //查询所有的用户信息 System.out.println("user = " + mapper.getAllUser()); } @Test public void testGetUserById(){ SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SelectMapper mapper = sqlSession.getMapper(SelectMapper.class); //查询用户id为3的用户信息 System.out.println("user = " + mapper.getUserById(3)); } }使用聚合函数查询单行信息SelectMapper接口 /* 查询用户信息的总记录数 */ public abstract Integer getCount();SelectMapper.xml select count(*) from t_user 查询用户信息为Map集合我们可以把查询出来的结果转换为一个map集合，以字段为键，字段对应的值为值。查询单条数据为Map集合SelectMapper接口 /* 根据id查询用户信息为一个map集合 */ public abstract Map<String, Object> getUserByIdToMap(@Param("id") Integer id);SelectMapper.xml <!--Map<String, Object> getUserByIdToMap(@Param("id") Integer id);--> select * from t_user where id = #{id} 查询多条数据为Map集合把map集合放入List集合中需要调整返回值，把多条数据的返回值放到一个List集合中，每一条转换的map放到了List集合中在接口方法上添加@MapKey注解 [label color="red"]@MapKey设置当前map集合的键，会把我们查询出来数据的某一个字段作为键(唯一)，查询出来数据的map集合作为值[/label]SelectMapper接口 /* 查询所有用户信息为map集合 */ //public abstract List<Map<String, Object>> getAllUserToMap(); @MapKey("id") public abstract Map<String, Object> getAllUserToMap();SelectMapper.xml <!--Map<String, Object> getAllUserToMap();--> select * from t_user 测试类最…

MyBatis笔记：搭建框架|核心文件详解|模板|封装SqlSession|获取参数值
type: item
date: 2023-08-20T15:51:00+00:00
description: 作为java最流行的持久层技术，即学习完JDBC之后的另一门技术，以轻量级和性能出色的半自动成为了核心三大技术SSM中的之一。本篇文章是基于Mybatis的基础学习，包括框架的搭建，优化，还有工具类的封装和模板的添加。搭建MyBatis在pom依赖文件中添加打包方式为jar包 <packaging>jar</packaging>在依赖文件中导入依赖因为我的SQL版本是8+版本，所以导入对应版本的依赖,并添加log4j的日志文件(下面的文章中有提及，引入依赖还需要导入配置文件xml在resources下) <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>核心配置文件创建核心配置文件对于一个框架，本质上来说就是 [label color="blue"]jar包 + 配置文件[/label] 文件没有默认的名字，但是建议是mybatis-config.xml,在整合SSM后，核心文件可以写可以不写，之后会交给Spring管理。 [label color="orange"]mybatis-config.xml[/label] ，核心配置文件应该创建在resources下<?xml version="1.0" encoding="UTF-8" ?> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="abc123"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>创建mapper接口(实现添加功能)Mybatis中的mapper接口相当于之前的DAO，以前的DAO有接口和实现类，而mapper只需要一个接口就可以，所谓面向接口编程。当我们通过mybatis中的方式去mapper创建接口对象，调用接口中的方法，就可以对应某个SQL语句。在接口中只需要添加操作数据库的方法即可。我们可以创建mapper接口，同DAO的命名，参考数据库表和实体类命名。映射文件尽量和我们的mapper接口名保持一致，只不过是一个xml文件。一个DAO类对应一个数据库表，一个mapper对应一个数据库表，一个mapper的映射文件对应一个mapper。 [label color="red"]MyBatis面向接口编程的两个一致：通过Mapper接口对应映射文件[/label]映射文件的namespace要和mapper接口的全类名保持一致映射文件中SQL语句的id要和mapper接口中的方法名一致UserMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <mapper namespace="com.atguigu.m…