首页
壁纸
留言板
友链
更多
统计归档
Search
1
TensorBoard:训练日志及网络结构可视化工具
12,588 阅读
2
主板开机跳线接线图【F_PANEL接线图】
7,063 阅读
3
Linux使用V2Ray 原生客户端
6,168 阅读
4
移动光猫获取超级密码&开启公网ipv6
4,730 阅读
5
NVIDIA 显卡限制功率
3,138 阅读
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
登录
/
注册
Search
标签搜索
好物分享
学习笔记
linux
MySQL
nvidia
typero
内网穿透
webdav
vps
java
cudann
gcc
cuda
树莓派
CNN
图像去雾
ssh安全
nps
暗通道先验
阿里云
jupiter
累计撰写
354
篇文章
累计收到
71
条评论
首页
栏目
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
页面
壁纸
留言板
友链
统计归档
搜索到
354
篇与
的结果
2022-03-19
蓝桥杯|历届真题:重复字符串
1.题目【问题描述】如果一个字符串S恰好可以由某个字符串重复K次得到,我们就称S是K次重复字符串。例如"abcabcabc”可以看作是"abc”重复3次得到,所以"abcabcabc”是3次重复字符串。同理"aaaaaa"既是2次重复字符串、又是3次重复字符串和6次重复字符串。现在给定一个字符串S,请你计算最少要修改其中几个字符,可以使S变为一个K次字符串?【输入格式】输入第一行包含一个整数K。第二行包含一个只含小写字母的字符串S。【输出格式】输出一个整数代表答案。如果S无法修改成K次重复字符串,输出-1。【样例输入】2 aabbaa【样例输出】2【评测用例规模与约定】对于所有评测用例,$1 \leq K \leq 100000,1 \leq |S| \leq 100000$。其中$|S|$表示$S$的长度。2. 题解2.1 思路分析先判断字符串长度是否是K的整数倍,不是则输出-1 若是,则将字符串分为K组,对每组的第i位统计得到各字符出现次数的最大值maxAppear 则要想将该字符串变为重复字符串在第i位上需要修改的最少的次数为K-maxAppear 对每一位需要做的最小修改进行求和即可得到最终的结果2.2 代码实现import java.util.*; public class Main { static Scanner scanner = new Scanner(System.in); public static void main(String[] args) { int K = scanner.nextInt(); String str = scanner.next(); int len = str.length(); char[] strChars = str.toCharArray(); if(len%K!=0){ System.out.println(-1); }else { int minChange = 0; int groupLen = len/K; for (int i = 0; i < groupLen; i++) {//将字符串分成K组 // 统计每组字符串第j位的哪个字符出现的次数最多得到次数 int maxAppear = 0; int[] charCounter = new int[26]; for (int j = 0; j < 26; j++) { charCounter[j]=0; } for (int j = 0; j < K; j++) { int tmpIndex = (int)(strChars[j*groupLen+i]-'a'); charCounter[tmpIndex] += 1; } for (int j = 0; j < 26; j++) { maxAppear = Math.max(maxAppear,charCounter[j]); } minChange += (K-maxAppear); } System.out.println(minChange); } } }2.3 提交结果1正确10.0062ms22.22MB输入 输出2正确10.0093ms22.30MBVIP特权3正确10.00109ms22.30MBVIP特权4正确10.0093ms22.26MBVIP特权5正确10.00109ms22.23MBVIP特权6正确10.0078ms22.29MBVIP特权7正确10.00140ms22.99MBVIP特权8正确10.00171ms25.62MBVIP特权9正确10.00125ms25.64MBVIP特权10错误0.00125ms24.57MBVIP特权暂未看出错在哪了参考资料http://lx.lanqiao.cn/problem.page?gpid=T2890
2022年03月19日
631 阅读
0 评论
0 点赞
2022-03-19
蓝桥杯|历届真题:成绩分析
暂无简介
2022年03月19日
545 阅读
0 评论
0 点赞
2022-03-17
蓝桥杯|历届真题:砝码称重
1.题目【问题描述】你有一架天平和 $N$个砝码,这 $N$个砝码重量依次是 $W_1, W_2, · · · , W_N$。 请你计算一共可以称出多少种不同的重量? 注意砝码可以放在天平两边。【输入格式】输入的第一行包含一个整数 $N$。第二行包含 $N$个整数:$W_1, W_2, · · · , W_N$。【输出格式】输出一个整数代表答案。【样例输入】3 1 4 6【样例输出】10【样例说明】能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。 1 = 1; 2 = 6 − 4 (天平一边放 6,另一边放 4); 3 = 4 − 1; 4 = 4; 5 = 6 − 1; 6 = 6; 7 = 1 + 6; 9 = 4 + 6 − 1; 10 = 4 + 6; 11 = 1 + 4 + 6。2. 题解2.1 思路分析增加一个砝码会增加的新的称重组合为 1.原来的各种称重组合+该砝码的重量 2.原来的各种称重组合-该砝码的重量(0除外) 3.该砝码本身的重量 当只有一个砝码的时候称重组合为该砝码本身的重量 由此形成了动态规划链2.2 代码实现import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int[] weights = new int[n]; for (int i = 0; i < n; i++) { weights[i] = scanner.nextInt(); } Set<Integer> set = new HashSet<>();//用来存储最终可能的组合,set可以自动去重 set.add(weights[0]); for (int i = 1; i < n; i++) { List<Integer> tmpList = new ArrayList<>(set); for(Integer item:tmpList){ if(item!=weights[i]){//避免出现称出0的情况 set.add(Math.abs(item-weights[i])); } set.add(item+weights[i]); } set.add(weights[i]); } System.out.println(set.size()); } }2.3 提交结果评测点序号评测结果得分CPU使用内存使用下载评测数据1正确10.0078ms22.23MB输入 输出2正确10.00109ms22.33MBVIP特权3正确10.0078ms22.32MBVIP特权4正确10.00140ms22.33MBVIP特权5正确10.0062ms22.68MBVIP特权6正确10.00109ms23.85MBVIP特权7正确10.0078ms26.24MBVIP特权8正确10.00171ms33.07MBVIP特权9正确10.00218ms64.09MBVIP特权10正确10.00234ms90.59MBVIP特权参考资料蓝桥杯试题 历届真题 砝码称重【第十二届】【省赛】【JAVAB组】http://lx.lanqiao.cn/problem.page?gpid=T2895
2022年03月17日
565 阅读
0 评论
0 点赞
2022-03-17
蓝桥杯|历届真题:异或数列
1.题目【问题描述】Alice 和 Bob 正在玩一个异或数列的游戏。初始时,Alice 和 Bob 分别有一个整数$a$和$b$,有一个给定的长度为 $n$ 的公共数列 $X_1,X_2, \dots, X_{n_i}$ 。Alice 和 Bob 轮流操作,Alice 先手,每步可以在以下两种选项中选一种:选项 1:从数列中选一个 $Xi$ 给 Alice 的数异或上,或者说令 $a$ 变为 $a ⊕ Xi$。(其中 $⊕$ 表示按位异或)选项 2:从数列中选一个$Xi$ 给 Bob 的数异或上,或者说令 $b$变为$ b ⊕ Xi$。每个数 $X_i$ 都只能用一次,当所有 $Xi$ 均被使用后($n$ 轮后)游戏结束。游戏结束时,拥有的数比较大的一方获胜,如果双方数值相同,即为平手。现在双方都足够聪明,都采用最优策略,请问谁能获胜?【输入格式】每个评测用例包含多组询问。询问之间彼此独立。输入的第一行包含一个整数 $T$,表示询问数。接下来 $T$ 行每行包含一组询问。其中第 i 行的第一个整数 $n_i$ 表示数列长度,随后 $n_i$个整数 $X_1,X_2, \dots, X_{n_i}$ 表示数列中的每个数。【输出格式】输出 $T$ 行,依次对应每组询问的答案。每行包含一个整数 1、0 或 −1 分别表示 Alice 胜、平局或败。【样例输入】4 1 1 1 0 2 2 1 7 992438 1006399 781139 985280 4729 872779 563580【样例输出】1 0 1 1【评测用例规模与约定】对于所有评测用例,$1 ≤T≤ 200000,1 ≤ \sum^T_{i=1}n_i ≤ 200000,0 ≤X_i <2^{20}$。2. 题解2.1 思路分析a和b起始值都是0 两人数字相同时,即a^b=0时平局 a^b=X1^X2^……^Xn 因此 X1^X2^……^Xn=0-->平局 否则谁数字大就赢,也就是当a和b(二进制)从高到低有一位不同时,是1的那个人赢; 用 bitCount[] 数组 统计X1……Xn每一位为1的次数; eg: 4(100)、6(110)、5(101),这3个数统计后 bitCount[3]=3,bitCount[2]=1,bitCount[1]=1 从最高位遍历bitCount[i] bitCount[i]==1,该位数只要一个1,Alice 先手,胜 bitCount[i]是偶数,无影响,不处理 bitCount[i]是奇数: n是偶数,奇数个1奇数个0,只要后手把0先选完,后手就获得最后一个1的支配权,后手胜 n是奇数,奇数个1偶数个0,先手把0先选完,先手获得最后一个1的支配权,先手胜2.2 代码实现import java.util.*; public class Main { static Scanner scanner = new Scanner(System.in); // 统计x1-xn对应的2进制数的每1位的1的总数量 public static int[] getBitCount(int[] arr){ int[] bitCount = new int[20];//存储每一位对应的1的总数 for (int i = 0; i < bitCount.length; i++) { bitCount[i]=0; } for (int i = 0; i < arr.length ; i++) { int tmp = arr[i]; int bit=0; while (tmp>0){ bitCount[bit++]+=tmp&1; tmp = tmp>>1 ; } } return bitCount; } public static int playOneEpoch(){ int arrNum = scanner.nextInt(); int [] arr = new int[arrNum]; int xor = 0; // 用于计算X1^X2^……^Xn for (int j = 0; j < arrNum; j++) { arr[j]= scanner.nextInt(); xor ^= arr[j]; } if(xor==0)return 0; // 统计所有位的1的个数 int[] bitCountOf1 = getBitCount(arr); // 从高位到低位进行判断 for (int i = bitCountOf1.length-1; i >=0; i--) { if(bitCountOf1[i]==0 || bitCountOf1[i]%2==0){//无需进行处理,对结果没有影响 continue; }else if(bitCountOf1[i]==1){//先手Alice选中直接获胜 return 1; }else if(bitCountOf1[i]%2==1){ // 该位只有奇数个1,则拿到最后一个1的一方获胜 if(arrNum%2==0){//总数为偶数 后手有最后一个1的支配权,后手胜利 return -1; }else{//总数为奇数 先手有最后一个1的支配权,先手胜利 return 1; } } } return 0; } public static void main(String[] args) { int T = scanner.nextInt(); int[] res = new int[T]; for (int i = 0; i < T; i++) { res[i] = playOneEpoch(); } for (int i = 0; i < T; i++) { System.out.println(res[i]); } } }2.3 提交结果评测点序号评测结果得分CPU使用内存使用下载评测数据1正确20.00656ms94.55MB输入 输出2正确20.00687ms95.83MBVIP特权3正确20.00734ms95.92MBVIP特权4正确20.00500ms92.60MBVIP特权5正确20.00406ms92.29MBVIP特权参考资料异或数列(c++位运算)蓝桥杯2021年第十二届省赛-异或数列http://lx.lanqiao.cn/detail.page?submitid=7217417第十二届蓝桥杯大赛软件赛省赛 Python 大学 A 组 试题
2022年03月17日
555 阅读
0 评论
0 点赞
2022-03-17
windows使用cmd快速删除文件夹
在windows下删除包含很多小文件的文件夹时使用图形界面进行删除会非常非常的慢,因此考虑探索实验cmd进行快速删除1.命令格式rmdir/rd [<drive>:]<path> [/s [/q]]名词解释rmdir删除指令:等价rddrive盘符path路径/s递归删除整个目录及其中文件/q在选定/s时开启quiet模式(不会提示删除相关信息)2.示例rd /s /q node_modules参考资料windows使用cmd快速删除一个文件夹https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/rmdirwindows使用cmd删除目录和文件(详细)
2022年03月17日
551 阅读
0 评论
0 点赞
2022-03-17
Linux批量修改文件后缀名
1.find+awk+xargs# .jpg-->.xml find ./ -name "*.jpg" | awk -F "." '{print $2}' | xargs -i -t mv ./{}.jpg ./{}.xml2.shell脚本#!/bin/bash find ./ -name *.jpg | while read i do echo "$i"; mv $i.jpg $i.xml done参考资料详解Linux批量更改文件后缀名
2022年03月17日
805 阅读
0 评论
0 点赞
2022-03-16
YOLO2VOC:将YOLO格式的数据转为xml格式的数据
当我们使用训练好的模型进行辅助标记的时候会有将YOLO检测到的数据转为VOC格式的xml的需求import xmltodict from xml.dom import minidom import copy # 待填充数据 frame_id = 3 # 视频帧编号 class_list = ['conveyor', 'refueller', 'aircraft', 'lounge', 'dining car', 'front of baggage car', 'tractor'] #类别信息 bbox_list = [[5, 824, 747, 912, 809], [3, 882, 549, 1365, 631], [4, 536, 768, 1023, 988], [1, 846, 687, 983, 747], [2, 2, 418, 126, 588], [0, 402, 847, 696, 987], [5, 844, 688, 984, 750]] # 准备相关模板 xml_base =""" <annotation> <folder>img</folder> <filename></filename> <path></path> <size> <width>1632</width> <height>1080</height> <depth>3</depth> </size> </annotation> """ obj_base =""" <object> <name></name> <bndbox> <xmin></xmin> <ymin></ymin> <xmax></xmax> <ymax></ymax> </bndbox> </object> """ # 读取模板并填充基本信息 xml_dict = xmltodict.parse(xml_base) frame_id_format = "{:>05d}".format(frame_id) filename = frame_id_format+".jpg" path = "./img/"+filename xml_dict["annotation"]["filename"] = filename xml_dict["annotation"]["path"] = path # 填入obj数据 xml_dict["annotation"]["object"] = [] obj_template = xmltodict.parse(obj_base)["object"] for bbox in bbox_list: tmp_obj = copy.deepcopy(obj_template) cls_id,x1,y1,x2,y2 = bbox tmp_obj["name"] = class_list[cls_id] tmp_obj["bndbox"]["xmin"] = x1 tmp_obj["bndbox"]["ymin"] = y1 tmp_obj["bndbox"]["xmax"] = x2 tmp_obj["bndbox"]["ymax"] = y2 xml_dict["annotation"]["object"].append(tmp_obj) xmlstr = xmltodict.unparse(xml_dict) xml_pretty_str = minidom.parseString(xmlstr).toprettyxml() print(xml_pretty_str)
2022年03月16日
593 阅读
0 评论
0 点赞
2022-03-16
快速使用Yolov5_DeepSort_Pytorch
1.项目地址https://github.com/mikel-brostrom/Yolov5_DeepSort_Pytorch2.运行步骤2.1 clone项目+ 配置环境git clone --recurse-submodules https://github.com/mikel-brostrom/Yolov5_DeepSort_Pytorch.gitpip install -r requirements.txt2.2训练YOLOv5子模块参考资料:快速使用YOLOv5进行训练VOC格式的数据集2.3 安装Torchreidcd deep_sort/deep rm -r reid git clone https://github.com/KaiyangZhou/deep-person-reid.git mv deep-person-reid reid cd reid python setup.py develop2.4 调用python track.py --source vid.mp4 --yolo_model yolov5s.pt --deep_sort_model osnet_x1_0 --save-txt --save-vid参考资料https://github.com/mikel-brostrom/Yolov5_DeepSort_Pytorchhttps://github.com/KaiyangZhou/deep-person-reid.git新版Yolov5_DeepSort_Pytorch使用ZQPei行人模型的方法
2022年03月16日
807 阅读
0 评论
0 点赞
2022-03-14
VOC格式数据集xml文件处理-类别增、删、改工具
1.删除xml中某一类别import xmltodict import os from xml.dom import minidom from tqdm import tqdm # filter class class_filter_list = ["person","baggage car"] xml_dir="./新建文件夹/"# VOC xml文件所在文件夹 # 加入进度条支持 pbar = tqdm(total=len(os.listdir(xml_dir))) pbar.set_description("VOC xml filter") # 设置前缀 # 逐一处理xml文件 for file in os.listdir(xml_dir): if not file.endswith(".xml"): pbar.update(1) continue # 过滤掉非xml文件 xml_path = os.path.join(xml_dir,file) # 拼接xml地址 # 读取xml文件+转为字典 with open(xml_path,'r',encoding="utf8") as f: xml_str = f.read() xml_dic = xmltodict.parse(xml_str) # 删除掉待过滤的类别 tmp_obj_list = xml_dic["annotation"]["object"] tmp_obj_list_fiter = [] for tmp_obj in tmp_obj_list: if not tmp_obj["name"] in class_filter_list: tmp_obj_list_fiter.append(tmp_obj) xml_dic["annotation"]["object"] = tmp_obj_list_fiter xmlstr = xmltodict.unparse(xml_dic) xml_pretty_str = minidom.parseString(xmlstr).toprettyxml() with open(xml_path,"w",encoding="utf8") as f: f.write(xml_pretty_str) pbar.update(1) pbar.close()2.指定帧区间添加特定bbox# 在固定位置添加指定的候选框 import xmltodict import os from xml.dom import minidom from tqdm import tqdm import copy xml_dir="./data_handle/xml/"# 待处理xml文件夹 # 待添加bbox xml = """ <object> <name>LuggageVehicle</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>212</xmin> <ymin>71</ymin> <xmax>284</xmax> <ymax>110</ymax> </bndbox> </object> """ xml_dict = xmltodict.parse(xml) obj = xml_dict["object"] cls_add = obj["name"] bndbox = obj["bndbox"] bbox_add = [int(bndbox["xmin"]),int(bndbox["ymin"]),int(bndbox["xmax"]),int(bndbox["ymax"])] # 起止帧id frame_begin = 84543 frame_end = 84919 # 加入进度条支持 pbar = tqdm(total=len(os.listdir(xml_dir))) pbar.set_description("VOC xml add class") # 设置前缀 # 逐一处理xml文件 for file in os.listdir(xml_dir): if not file.endswith(".xml"): pbar.update(1) continue # 过滤掉非xml文件 if int(file[:-4])<frame_begin or int(file[:-4])>frame_end: pbar.update(1) continue xml_path = os.path.join(xml_dir,file) # 拼接地址 # 读取xml文件+转为字典 with open(xml_path,'r',encoding="utf8") as f: xml_str = f.read() xml_dic = xmltodict.parse(xml_str) # 添加类别 tmp_obj_list = xml_dic["annotation"]["object"] tmp_obj = copy.deepcopy(tmp_obj_list[0]) tmp_obj["name"] = cls_add tmp_obj["bndbox"]["xmin"]=bbox_add[0] tmp_obj["bndbox"]["ymin"]=bbox_add[1] tmp_obj["bndbox"]["xmax"]=bbox_add[2] tmp_obj["bndbox"]["ymax"]=bbox_add[3] tmp_obj_list.append(tmp_obj) xml_dic["annotation"]["object"] = tmp_obj_list xmlstr = xmltodict.unparse(xml_dic) xml_pretty_str = minidom.parseString(xmlstr).toprettyxml() with open(xml_path,"w",encoding="utf8") as f: f.write(xml_pretty_str) pbar.update(1) pbar.close()3.指定帧区间删除特定的bbox""" IOU计算 + input + box1:[box1_x1,box1_y1,box1_x2,box1_y2] + box2:[box2_x1,box2_y1,box2_x2,box2_y2] + output + iou值 """ def cal_iou(box1,box2): # 判断是否能相交 if abs(box2[2]+box2[0]-box1[2]-box1[0])>box2[2]-box2[0]+box1[2]-box1[0]: return 0 if abs(box2[3]+box2[1]-box1[3]-box1[1])>box2[3]-box2[1]+box1[3]-box1[1]: return 0 # 求相交区域左上角的坐标和右下角的坐标 box_intersect_x1 = max(box1[0], box2[0]) box_intersect_y1 = max(box1[1], box2[1]) box_intersect_x2 = min(box1[2], box2[2]) box_intersect_y2 = min(box1[3], box2[3]) # 求二者相交的面积 area_intersect = (box_intersect_y2 - box_intersect_y1) * (box_intersect_x2 - box_intersect_x1) # 求box1,box2的面积 area_box1 = (box1[2] - box1[0]) * (box1[3] - box1[1]) area_box2 = (box2[2] - box2[0]) * (box2[3] - box2[1]) # 求二者相并的面积 area_union = area_box1 + area_box2 - area_intersect # 计算iou(交并比) iou = area_intersect / area_union return iou# 删除指定的bbox import xmltodict import os from xml.dom import minidom from tqdm import tqdm xml_dir="./data_handle/xml/"# VOC xml文件所在文件夹 # 匹配待删除的bbox xml = """ <object> <name>Person</name> <bndbox> <xmin>901</xmin> <ymin>6</ymin> <xmax>920</xmax> <ymax>31</ymax> </bndbox> </object> """ xml_dict = xmltodict.parse(xml) obj = xml_dict["object"] cls_filter = obj["name"] bndbox = obj["bndbox"] bbox_filter = [int(bndbox["xmin"]),int(bndbox["ymin"]),int(bndbox["xmax"]),int(bndbox["ymax"])] iou_filter = 0.6 # 起止帧id frame_begin = 85000 frame_end = 85427 # 加入进度条支持 pbar = tqdm(total=len(os.listdir(xml_dir))) pbar.set_description("VOC xml filter") # 设置前缀 # 逐一处理xml文件 for file in os.listdir(xml_dir): if not file.endswith(".xml"): pbar.update(1) continue # 过滤掉非xml文件 if int(file[:-4])<frame_begin or int(file[:-4])>frame_end: pbar.update(1) continue xml_path = os.path.join(xml_dir,file) # 拼接xml地址 # 读取xml文件+转为字典 with open(xml_path,'r',encoding="utf8") as f: xml_str = f.read() xml_dic = xmltodict.parse(xml_str) # 删除掉待过滤的类别 tmp_obj_list = xml_dic["annotation"]["object"] tmp_obj_list_fiter = [] for tmp_obj in tmp_obj_list: tmp_bndbox = tmp_obj["bndbox"] tmp_obj_bbox = [int(tmp_bndbox["xmin"]),int(tmp_bndbox["ymin"]),int(tmp_bndbox["xmax"]),int(tmp_bndbox["ymax"])] if tmp_obj["name"]==cls_filter and cal_iou(bbox_filter,tmp_obj_bbox)>iou_filter: print(cal_iou(bbox_filter,tmp_obj_bbox)) pass else: tmp_obj_list_fiter.append(tmp_obj) xml_dic["annotation"]["object"] = tmp_obj_list_fiter xmlstr = xmltodict.unparse(xml_dic) xml_pretty_str = minidom.parseString(xmlstr).toprettyxml() with open(xml_path,"w",encoding="utf8") as f: f.write(xml_pretty_str) pbar.update(1) pbar.close()4.指定帧区间修改指定bbox的类别""" IOU计算 + input + box1:[box1_x1,box1_y1,box1_x2,box1_y2] + box2:[box2_x1,box2_y1,box2_x2,box2_y2] + output + iou值 """ def cal_iou(box1,box2): # 判断是否能相交 if abs(box2[2]+box2[0]-box1[2]-box1[0])>box2[2]-box2[0]+box1[2]-box1[0]: return 0 if abs(box2[3]+box2[1]-box1[3]-box1[1])>box2[3]-box2[1]+box1[3]-box1[1]: return 0 # 求相交区域左上角的坐标和右下角的坐标 box_intersect_x1 = max(box1[0], box2[0]) box_intersect_y1 = max(box1[1], box2[1]) box_intersect_x2 = min(box1[2], box2[2]) box_intersect_y2 = min(box1[3], box2[3]) # 求二者相交的面积 area_intersect = (box_intersect_y2 - box_intersect_y1) * (box_intersect_x2 - box_intersect_x1) # 求box1,box2的面积 area_box1 = (box1[2] - box1[0]) * (box1[3] - box1[1]) area_box2 = (box2[2] - box2[0]) * (box2[3] - box2[1]) # 求二者相并的面积 area_union = area_box1 + area_box2 - area_intersect # 计算iou(交并比) iou = area_intersect / area_union return iou # 修改指定框的类别 import xmltodict import os from xml.dom import minidom from tqdm import tqdm xml_dir="./data_handle/xml/"# VOC xml文件所在文件夹 # 匹配待修改的bbox xml = """ <object> <name>RefuelVehicle</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>106</xmin> <ymin>389</ymin> <xmax>237</xmax> <ymax>542</ymax> </bndbox> </object> """ xml_dict = xmltodict.parse(xml) obj = xml_dict["object"] cls_filter_src = obj["name"] bndbox = obj["bndbox"] bbox_filter = [int(bndbox["xmin"]),int(bndbox["ymin"]),int(bndbox["xmax"]),int(bndbox["ymax"])] cls_filter_dst = "LuggageVehicle" iou_filter = 0.99 # 起止帧id frame_begin = 59680 frame_end = 60000 # 加入进度条支持 pbar = tqdm(total=len(os.listdir(xml_dir))) pbar.set_description("VOC xml filter") # 设置前缀 # 逐一处理xml文件 for file in os.listdir(xml_dir): if not file.endswith(".xml"): pbar.update(1) continue # 过滤掉非xml文件 if int(file[:-4])<frame_begin or int(file[:-4])>frame_end: pbar.update(1) continue xml_path = os.path.join(xml_dir,file) # 拼接原始地址 # 读取xml文件+转为字典 with open(xml_path,'r',encoding="utf8") as f: xml_str = f.read() xml_dic = xmltodict.parse(xml_str) # 删除掉待过滤的类别 tmp_obj_list = xml_dic["annotation"]["object"] tmp_obj_list_fiter = [] for tmp_obj in tmp_obj_list: tmp_bndbox = tmp_obj["bndbox"] tmp_obj_bbox = [int(tmp_bndbox["xmin"]),int(tmp_bndbox["ymin"]),int(tmp_bndbox["xmax"]),int(tmp_bndbox["ymax"])] if tmp_obj["name"]==cls_filter_src and cal_iou(bbox_filter,tmp_obj_bbox)>iou_filter: tmp_obj["name"]=cls_filter_dst tmp_obj_list_fiter.append(tmp_obj) else: tmp_obj_list_fiter.append(tmp_obj) xml_dic["annotation"]["object"] = tmp_obj_list_fiter xmlstr = xmltodict.unparse(xml_dic) xml_pretty_str = minidom.parseString(xmlstr).toprettyxml() with open(xml_path,"w",encoding="utf8") as f: f.write(xml_pretty_str) pbar.update(1) pbar.close()
2022年03月14日
619 阅读
0 评论
0 点赞
2022-03-13
树莓派4b安装miniconda armv8 64位
1.使用uname -a查看你操作系统的版本pi@raspberrypi:~ $ uname -a Linux raspberrypi 5.10.52-v8+ #1441 SMP PREEMPT Tue Aug 3 18:14:03 BST 2021 aarch64 GNU/Linux2.下载安装包然后去这个清华镜像网站选择对应你操作系统的版本下载:https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/因为我是64位的,所以我选择Linux-aarch64版本的下载:wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py37_4.9.2-Linux-aarch64.sh注意:目前大于4.9版本的miniconda不适配树莓派arm64架构操作系统,安装后无法正常使用!建议按照我上面给的miniconda版本安装!3.执行安装并激活环境bash Miniconda3-py37_4.9.2-Linux-aarch64.sh source ~/.bashrc4.验证(base) pi@raspberrypi:/data $ conda -V conda 4.9.2参考资料树莓派4b安装miniconda
2022年03月13日
1,439 阅读
1 评论
0 点赞
2022-03-11
Linux配置临时IP地址
偶然遇到了需要为某个网卡添加一个临时IP的问题1.添加临时IP ifconfig enp3s0:0 10.1.9.41 netmask 255.255.255.0 # 或者 ip addr add 10.1.9.41/24 dev enp3s0:02.删除临时IP ip addr delete 10.1.9.41 dev enp3s0:0参考资料ifconfig-网络管理命令ifconfig增加一个临时IP方法及删除方法
2022年03月11日
783 阅读
0 评论
0 点赞
2022-03-08
java实现大数的十六进制转八进制
1.问题描述问题描述 给定n个十六进制正整数,输出它们对应的八进制数。输入格式 输入的第一行为一个正整数n (1<=n<=10)。 接下来n行,每行一个由0-9、大写字母A-F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。输出格式 输出n行,每行为输入对应的八进制正整数。注意 输入的十六进制数不会有前导0,比如012A。 输出的八进制数也不能有前导0。样例输入 2 39 123ABC样例输出 71 4435274提示 先将十六进制数转换成某进制数,再由某进制数转换成八进制。2.代码实现import java.util.Scanner; public class Main { //16进制转2进制 public static String hex2Bin(String hex){ StringBuilder sb = new StringBuilder(); char[] hex_chars = hex.toCharArray(); for(char item:hex_chars){ switch (item){ case '0':sb.append("0000");break; case '1':sb.append("0001");break; case '2':sb.append("0010");break; case '3':sb.append("0011");break; case '4':sb.append("0100");break; case '5':sb.append("0101");break; case '6':sb.append("0110");break; case '7':sb.append("0111");break; case '8':sb.append("1000");break; case '9':sb.append("1001");break; case 'A':sb.append("1010");break; case 'B':sb.append("1011");break; case 'C':sb.append("1100");break; case 'D':sb.append("1101");break; case 'E':sb.append("1110");break; case 'F':sb.append("1111");break; default:sb.append("ERROR");break; } } return sb.toString(); } //2进制转8进制 public static String bin2Otc(String bin){ StringBuilder sb = new StringBuilder(); //将字符串的长度补齐到3的整数倍 int binLength = bin.length(); if(binLength%3==1){ bin = "00"+bin; }else if(binLength%3==2){ bin = "0"+bin; } //每3位转换出一个8进制数 for(int i=0;i<bin.length()/3;i++){ String octItem = bin.substring(i*3,i*3+3); switch (octItem){ case "000":sb.append("0");break; case "001":sb.append("1");break; case "010":sb.append("2");break; case "011":sb.append("3");break; case "100":sb.append("4");break; case "101":sb.append("5");break; case "110":sb.append("6");break; case "111":sb.append("7");break; default:sb.append("ERROR");break; } } return sb.toString(); } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int num_count = scanner.nextInt(); String []nums = new String[num_count]; for(int i=0;i<num_count;i++){ nums[i] = scanner.next(); } for(int i=0;i<num_count;i++){ String bin = hex2Bin(nums[i]); String oct = bin2Otc(bin); if(oct.charAt(0)=='0'){ oct = oct.substring(1); } System.out.println(oct); } } }参考资料1.http://lx.lanqiao.cn/problem.page?gpid=T51
2022年03月08日
612 阅读
0 评论
0 点赞
2022-03-06
MyBatis学习笔记
1.简介1.1 什么是MyBatisMyBatis 是一款优秀的持久层框架MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github .Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.htmlGitHub : https://github.com/mybatis/mybatis-31.2 持久化持久化是将程序数据在持久状态和瞬时状态间转换的机制。即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。JDBC就是一种持久化机制。文件IO也是一种持久化机制。在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。为什么需要持久化服务呢?那是由于内存本身的缺陷引起的内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。1.3 持久层什么是持久层?完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。不过这里有一个字需要特别强调,也就是所谓的“层”。对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现.与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。【说白了就是用来操作数据库存在的!】1.4 为什么需要MybatisMybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等... , 通过框架可以减少重复代码,提高开发效率 .MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别MyBatis的优点简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。提供xml标签,支持编写动态sql。.......最重要的一点,使用的人多!公司需要!2.MyBatis第一个程序思路流程:搭建环境-->导入Mybatis--->编写代码--->测试2.1 搭建实验数据库CREATE DATABASE `mybatis`; USE `mybatis`; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(20) NOT NULL, `name` varchar(30) DEFAULT NULL, `pwd` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');2.2 导入MyBatis相关 jar 包GitHub上找<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>2.3 编写MyBatis核心配置文件查看帮助文档<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/kuang/dao/userMapper.xml"/> </mappers> </configuration>2.4 编写MyBatis工具类查看帮助文档import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //获取SqlSession连接 public static SqlSession getSession(){ return sqlSessionFactory.openSession(); } }2.5 创建实体类public class User { private int id; //id private String name; //姓名 private String pwd; //密码 //构造,有参,无参 //set/get //toString() }2.6 编写Mapper接口类import com.kuang.pojo.User; import java.util.List; public interface UserMapper { List<User> selectUser(); }2.7 编写Mapper.xml配置文件namespace 十分重要,不能写错!<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.dao.UserMapper"> <select id="selectUser" resultType="com.kuang.pojo.User"> select * from user </select> </mapper>2.8 编写测试类Junit 包测试public class MyTest { @Test public void selectUser() { SqlSession session = MybatisUtils.getSession(); //方法一: //List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser"); //方法二: UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user: users){ System.out.println(user); } session.close(); } }2.9 运行测试,成功的查询出来的我们的数据,ok!2.10 问题说明可能出现问题说明:Maven静态资源过滤问题<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources>有了MyBatis以后再也不用写原生的JDBC代码了,舒服!3.CRUD操作3.1 selectselect标签是mybatis中最常用的标签之一select语句有很多属性可以详细配置每一条SQL语句SQL语句返回值类型。【完整的类名或者别名】传入SQL语句的参数类型 。【万能的Map,可以多尝试使用】命名空间中唯一的标识符接口中的方法名与映射文件中的SQL语句ID 一一对应idparameterTyperesultType需求:根据id查询用户1、在UserMapper中添加对应方法public interface UserMapper { //查询全部用户 List<User> selectUser(); //根据id查询用户 User selectUserById(int id); }2、在UserMapper.xml中添加Select语句<select id="selectUserById" resultType="com.kuang.pojo.User"> select * from user where id = #{id} </select>3、测试类中测试@Test public void tsetSelectUserById() { SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }课堂练习:根据 密码 和 名字 查询用户思路一:直接在方法中传递参数1、在接口方法的参数前加 @Param属性2、Sql语句编写的时候,直接取@Param中设置的值即可,不需要单独设置参数类型//通过密码和名字查询用户 User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd); /* <select id="selectUserByNP" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select> */思路二:使用万能的Map1、在接口方法中,参数直接传递Map;User selectUserByNP2(Map<String,Object> map);2、编写sql语句的时候,需要传递参数类型,参数类型为map<select id="selectUserByNP2" parameterType="map" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select>3、在使用方法的时候,Map的 key 为 sql中取的值即可,没有顺序要求!Map<String, Object> map = new HashMap<String, Object>(); map.put("username","小明"); map.put("pwd","123456"); User user = mapper.selectUserByNP2(map);总结:如果参数过多,我们可以考虑直接使用Map实现,如果参数比较少,直接传递参数即可3.2 insert我们一般使用insert标签进行插入操作,它的配置和select标签差不多!需求:给数据库增加一个用户1、在UserMapper接口中添加对应的方法//添加一个用户 int addUser(User user);2、在UserMapper.xml中添加insert语句<insert id="addUser" parameterType="com.kuang.pojo.User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}) </insert>3、测试@Test public void testAddUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(5,"王五","zxcvbn"); int i = mapper.addUser(user); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }注意点:增、删、改操作需要提交事务!3.3update我们一般使用update标签进行更新操作,它的配置和select标签差不多!需求:修改用户的信息1、同理,编写接口方法//修改一个用户 int updateUser(User user);2、编写对应的配置文件SQL<update id="updateUser" parameterType="com.kuang.pojo.User"> update user set name=#{name},pwd=#{pwd} where id = #{id} </update>3、测试@Test public void testUpdateUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); user.setPwd("asdfgh"); int i = mapper.updateUser(user); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }3.4 delete我们一般使用delete标签进行删除操作,它的配置和select标签差不多!需求:根据id删除一个用户1、同理,编写接口方法//根据id删除用户 int deleteUser(int id);2、编写对应的配置文件SQL<delete id="deleteUser" parameterType="int"> delete from user where id = #{id} </delete>3、测试@Test public void testDeleteUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int i = mapper.deleteUser(5); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }小结:所有的增删改操作都需要提交事务!接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!有时候根据业务的需求,可以考虑使用map传递参数!为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!3.5 思考题模糊查询like语句该怎么写?第1种:在Java代码中添加sql通配符。string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like #{value} </select>第2种:在sql语句中拼接通配符,会引起sql注入string wildcardname = “smi”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like "%"#{value}"%" </select>4.配置解析4.1 核心配置文件mybatis-config.xml 系统核心配置文件MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。能配置的内容如下:configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器) <!-- 注意元素节点的顺序!顺序不对会报错 -->我们可以阅读 mybatis-config.xml 上面的dtd的头文件!4.2 environments元素<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定)子元素节点:environmentdataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。数据源是必须配置的。有三种内建的数据源类型type="[UNPOOLED|POOLED|JNDI]")unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等....详情:点击查看官方文档这两种事务管理器类型都不需要设置任何属性。具体的一套环境,通过设置id进行区别,id保证唯一!子元素节点:transactionManager - [ 事务管理器 ]<!-- 语法 --> <transactionManager type="[ JDBC | MANAGED ]"/>子元素节点:数据源(dataSource)4.3 mappers元素mappers映射器 : 定义映射SQL语句文件既然 MyBatis 的行为其他元素已经配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。映射器是MyBatis中最核心的组件之一,在MyBatis 3之前,只支持xml映射器,即:所有的SQL语句都必须在xml文件中配置。而从MyBatis 3开始,还支持接口映射器,这种映射器方式允许以Java代码的方式注解定义SQL语句,非常简洁。引入资源方式<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers> <!-- 使用完全限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers> <!-- 使用映射器接口实现类的完全限定类名 需要配置文件名称和接口名称一致,并且位于同一目录下 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> </mappers> <!-- 将包内的映射器接口实现全部注册为映射器 但是需要配置文件名称和接口名称一致,并且位于同一目录下 --> <mappers> <package name="org.mybatis.builder"/> </mappers>Mapper文件<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mapper.UserMapper"> </mapper>namespace中文意思:命名空间,作用如下:namespace的命名必须跟某个接口同名接口中的方法与映射文件中sql语句id应该一一对应namespace和子元素的id联合保证唯一 , 区别不同的mapper绑定DAO接口namespace命名规则 : 包名+类名MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦。4.4 Properties优化数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。具体的官方文档我们来优化我们的配置文件第一步 ; 在资源目录下新建一个db.propertiesdriver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8 username=root password=123456第二步 : 将文件导入properties 配置文件<configuration> <!--导入properties文件--> <properties resource="db.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>更多操作,可以查看官方文档!【演示带领学习】配置文件优先级问题新特性:使用占位符4.5 typeAliases优化类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。<!--配置别名,注意顺序--> <typeAliases> <typeAlias type="com.kuang.pojo.User" alias="User"/> </typeAliases>当这样配置时,User可以用在任何使用com.kuang.pojo.User的地方。也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:<typeAliases> <package name="com.kuang.pojo"/> </typeAliases>每一个在包 com.kuang.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。若有注解,则别名为其注解值。见下面的例子:@Alias("user") public class User { ... }去官网查看一下Mybatis默认的一些类型别名!4.6 其他配置浏览设置设置(settings)相关 => 查看帮助文档懒加载日志实现缓存开启关闭一个配置完整的 settings 元素的示例如下:<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>类型处理器无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】对象工厂MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】4.7 生命周期和作用域作用域(Scope)和生命周期理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。我们可以先画一个流程图,分析一下Mybatis的执行过程!作用域理解SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try...catch...finally... 语句来保证其正确关闭。所以 SqlSession 的最佳的作用域是请求或方法作用域。5.ResultMap5.1 查询为null问题要解决的问题:属性名和字段名不一致环境:新建一个项目,将之前的项目拷贝过来1、查看之前的数据库的字段名2、Java中的实体类设计public class User { private int id; //id private String name; //姓名 private String password; //密码和数据库不一样! //构造 //set/get //toString() }3、接口//根据id查询用户 User selectUserById(int id);4、mapper映射文件<select id="selectUserById" resultType="user"> select * from user where id = #{id} </select>5、测试@Test public void testSelectUserById() { SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }结果:User{id=1, name='狂神', password='null'}查询出来发现 password 为空 . 说明出现了问题!分析:select * from user where id = #{id} 可以看做select id,name,pwd from user where id = #{id}mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到setPwd() , 所以password返回null ; 【自动映射】5.2 解决方案方案一:为列名指定别名 , 别名和java实体类的属性名一致 .<select id="selectUserById" resultType="User"> select id , name , pwd as password from user where id = #{id} </select>方案二:使用结果集映射->ResultMap 【推荐】<resultMap id="UserMap" type="User"> <!-- id为主键 --> <id column="id" property="id"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="selectUserById" resultMap="UserMap"> select id , name , pwd from user where id = #{id} </select>5.3 ResultMap自动映射resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达数千行的代码。ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。你已经见过简单映射语句的示例了,但并没有显式指定 resultMap。比如:<select id="selectUserById" resultType="map"> select id , name , pwd from user where id = #{id} </select>上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。虽然在大部分情况下都够用,但是 HashMap 不是一个很好的模型。你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 对象)作为模型。ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。手动映射1、返回值类型为resultMap<select id="selectUserById" resultMap="UserMap"> select id , name , pwd from user where id = #{id} </select>2、编写resultMap,实现手动映射!<resultMap id="UserMap" type="User"> <!-- id为主键 --> <id column="id" property="id"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap>如果世界总是这么简单就好了。但是肯定不是的,数据库中,存在一对多,多对一的情况,我们之后会使用到一些高级的结果集映射,association,collection这些,我们将在之后讲解,今天你们需要把这些知识都消化掉才是最重要的!理解结果集映射的这个概念!6.日志6.1 日志工厂思考:我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率?如果一个 数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。对于以往的开发过程,我们会经常使用到debug模式来调节,跟踪我们的代码执行过程。但是现在使用Mybatis是基于接口,配置文件的源代码执行过程。因此,我们必须选择日志工具来作为我们开发,调节程序的工具。Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:SLF4JApache Commons LoggingLog4j 2Log4jJDK logging具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。标准日志实现指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>测试,可以看到控制台有大量的输出!我们可以通过这些输出来判断程序到底哪里出了Bug6.2 Log4j简介:Log4j是Apache的一个开源项目通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件....我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。使用步骤:1、导入log4j的包<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>2、配置文件编写,在resources下新建log4j.properties#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG3、setting设置日志实现<settings> <setting name="logImpl" value="LOG4J"/> </settings>4、在程序中使用Log4j进行输出!//注意导包:org.apache.log4j.Logger static Logger logger = Logger.getLogger(MyTest.class); @Test public void selectUser() { logger.info("info:进入selectUser方法"); logger.debug("debug:进入selectUser方法"); logger.error("error: 进入selectUser方法"); SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user: users){ System.out.println(user); } session.close(); }5、测试,看控制台输出!使用Log4j 输出日志可以看到还生成了一个日志的文件 【需要修改file的日志级别】7.分页7.1 limit实现分页思考:为什么需要分页?在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。使用Limit实现分页#语法 SELECT * FROM table LIMIT stratIndex,pageSize SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 #为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1: SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last. #如果只给定一个参数,它表示返回最大的记录行数目: SELECT * FROM table LIMIT 5; //检索前 5 个记录行 #换句话说,LIMIT n 等价于 LIMIT 0,n。 步骤:1、修改Mapper文件<select id="selectUser" parameterType="map" resultType="user"> select * from user limit #{startIndex},#{pageSize} </select>2、Mapper接口,参数为map//选择全部用户实现分页 List<User> selectUser(Map<String,Integer> map);3、在测试类中传入参数测试推断:起始位置 = (当前页面 - 1 ) * 页面大小//分页查询 , 两个参数startIndex , pageSize @Test public void testSelectUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int currentPage = 1; //第几页 int pageSize = 2; //每页显示几个 Map<String,Integer> map = new HashMap<String,Integer>(); map.put("startIndex",(currentPage-1)*pageSize); map.put("pageSize",pageSize); List<User> users = mapper.selectUser(map); for (User user: users){ System.out.println(user); } session.close(); }7.2 RowBounds分页我们除了使用Limit在SQL层面实现分页,也可以使用RowBounds在Java代码层面实现分页,当然此种方式作为了解即可。我们来看下如何实现的!步骤:1、mapper接口//选择全部用户RowBounds实现分页 List<User> getUserByRowBounds();2、mapper文件<select id="getUserByRowBounds" resultType="user"> select * from user </select>3、测试类在这里,我们需要使用RowBounds类@Test public void testUserByRowBounds() { SqlSession session = MybatisUtils.getSession(); int currentPage = 2; //第几页 int pageSize = 2; //每页显示几个 RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize); //通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了] List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds); for (User user: users){ System.out.println(user); } session.close(); }7.3 PageHelper了解即可,可以自己尝试使用官方文档:https://pagehelper.github.io/在MyBatisPlus中,我们也讲解到了分页实现,所以实现方式很多,看自己的理解和熟练程度进行掌握即可!8.使用注解开发8.1 面向接口编程大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。关于接口的理解接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。接口的本身反映了系统设计人员对系统的抽象理解。接口应有两类:第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);一个体有可能有多个抽象面。抽象体与抽象面是有区别的。三个面向区别面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构8.2 利用注解开发mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建sql 类型主要分成 :@select ()@update ()@Insert ()@delete ()注意:利用注解开发就不需要mapper.xml映射文件了 .1、我们在我们的接口中添加注解//查询全部用户 @Select("select id,name,pwd password from user") public List<User> getAllUser();2、在mybatis的核心配置文件中注入<!--使用class绑定接口--> <mappers> <mapper class="com.kuang.mapper.UserMapper"/> </mappers>3、我们去进行测试@Test public void testGetAllUser() { SqlSession session = MybatisUtils.getSession(); //本质上利用了jvm的动态代理机制 UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.getAllUser(); for (User user : users){ System.out.println(user); } session.close(); }4、利用Debug查看本质5、本质上利用了jvm的动态代理机制6、Mybatis详细的执行流程8.3 注解增删改改造MybatisUtils工具类的getSession( ) 方法,重载实现。 //获取SqlSession连接 public static SqlSession getSession(){ return getSession(true); //事务自动提交 } public static SqlSession getSession(boolean flag){ return sqlSessionFactory.openSession(flag); }【注意】确保实体类和数据库字段对应查询:1、编写接口方法注解//根据id查询用户 @Select("select * from user where id = #{id}") User selectUserById(@Param("id") int id);2、测试@Test public void testSelectUserById() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }新增:1、编写接口方法注解//添加一个用户 @Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})") int addUser(User user);2、测试@Test public void testAddUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(6, "秦疆", "123456"); mapper.addUser(user); session.close(); }修改:1、编写接口方法注解//修改一个用户 @Update("update user set name=#{name},pwd=#{pwd} where id = #{id}") int updateUser(User user);2、测试@Test public void testUpdateUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(6, "秦疆", "zxcvbn"); mapper.updateUser(user); session.close(); }删除:1、编写接口方法注解//根据id删除用 @Delete("delete from user where id = #{id}") int deleteUser(@Param("id")int id);2、测试@Test public void testDeleteUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); mapper.deleteUser(6); session.close(); }【注意点:增删改一定记得对事务的处理】8.4 关于@Param@Param注解用于给方法参数起一个名字。以下是总结的使用原则:在方法只接受一个参数的情况下,可以不使用@Param。在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。如果参数是 JavaBean , 则不能使用@Param。不使用@Param注解时,参数只能有一个,并且是Javabean。8.5 #与$的区别\#{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】INSERT INTO user (name) VALUES (#{name}); INSERT INTO user (name) VALUES (?);${} 的作用是直接进行字符串替换INSERT INTO user (name) VALUES ('${name}'); INSERT INTO user (name) VALUES ('kuangshen');使用注解和配置文件协同开发,才是MyBatis的最佳实践!9.多对一处理多对一的理解:多个学生对应一个老师如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!9.1 数据库设计CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); CREATE TABLE `student` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');9.2 搭建测试环境1、IDEA安装Lombok插件2、引入Maven依赖<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency>3、在代码中增加注解@Data //GET,SET,ToString,有参,无参构造 public class Teacher { private int id; private String name; } @Data public class Student { private int id; private String name; //多个学生可以是同一个老师,即多对一 private Teacher teacher; }4、编写实体类对应的Mapper接口 【两个】无论有没有需求,都应该写上,以备后来之需!public interface StudentMapper { } public interface TeacherMapper { }5、编写Mapper接口对应的 mapper.xml配置文件 【两个】无论有没有需求,都应该写上,以备后来之需!<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mapper.StudentMapper"> </mapper> <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mapper.TeacherMapper"> </mapper>9.3 按查询嵌套处理1、给StudentMapper接口增加方法//获取所有学生及对应老师的信息 public List<Student> getStudents();2、编写对应的Mapper文件<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mapper.StudentMapper"> <!-- 需求:获取所有学生及对应老师的信息 思路: 1. 获取所有学生的信息 2. 根据获取的学生信息的老师ID->获取该老师的信息 3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询? 1. 做一个结果集映射:StudentTeacher 2. StudentTeacher结果集的类型为 Student 3. 学生中老师的属性为teacher,对应数据库中为tid。 多个 [1,...)学生关联一个老师=> 一对一,一对多 4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询 --> <select id="getStudents" resultMap="StudentTeacher"> select * from student </select> <resultMap id="StudentTeacher" type="Student"> <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名--> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置: column="{key=value,key=value}" 其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。 --> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} </select> </mapper>3、编写完毕去Mybatis配置文件中,注册Mapper!4、注意点说明:<resultMap id="StudentTeacher" type="Student"> <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名--> <association property="teacher" column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/> </resultMap> <!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置: column="{key=value,key=value}" 其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。 --> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} and name = #{name} </select>5、测试@Test public void testGetStudents(){ SqlSession session = MybatisUtils.getSession(); StudentMapper mapper = session.getMapper(StudentMapper.class); List<Student> students = mapper.getStudents(); for (Student student : students){ System.out.println( "学生名:"+ student.getName() +"\t老师:"+student.getTeacher().getName()); } }9.4 按结果嵌套处理除了上面这种方式,还有其他思路吗?我们还可以按照结果进行嵌套处理;1、接口方法编写public List<Student> getStudents2();2、编写对应的mapper文件<!-- 按查询结果嵌套处理 思路: 1. 直接查询出结果,进行结果集的映射 --> <select id="getStudents2" resultMap="StudentTeacher2" > select s.id sid, s.name sname , t.name tname from student s,teacher t where s.tid = t.id </select> <resultMap id="StudentTeacher2" type="Student"> <id property="id" column="sid"/> <result property="name" column="sname"/> <!--关联对象property 关联对象在Student实体类中的属性--> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>3、去mybatis-config文件中注入【此处应该处理过了】4、测试@Test public void testGetStudents2(){ SqlSession session = MybatisUtils.getSession(); StudentMapper mapper = session.getMapper(StudentMapper.class); List<Student> students = mapper.getStudents2(); for (Student student : students){ System.out.println( "学生名:"+ student.getName() +"\t老师:"+student.getTeacher().getName()); } }9.5 小结按照查询进行嵌套处理就像SQL中的子查询按照结果进行嵌套处理就像SQL中的联表查询10.一对多的处理一对多的理解:一个老师拥有多个学生如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!10.1 实体类编写@Data public class Student { private int id; private String name; private int tid; } @Data public class Teacher { private int id; private String name; //一个老师多个学生 private List<Student> students; }..... 和之前一样,搭建测试的环境!10.2 按结果嵌套处理1、TeacherMapper接口编写方法//获取指定老师,及老师下的所有学生 public Teacher getTeacher(int id);2、编写接口对应的Mapper配置文件<mapper namespace="com.kuang.mapper.TeacherMapper"> <!-- 思路: 1. 从学生表和老师表中查出学生id,学生姓名,老师姓名 2. 对查询出来的操作做结果集映射 1. 集合的话,使用collection! JavaType和ofType都是用来指定对象类型的 JavaType是用来指定pojo中属性的类型 ofType指定的是映射到list集合属性中pojo的类型。 --> <select id="getTeacher" resultMap="TeacherStudent"> select s.id sid, s.name sname , t.name tname, t.id tid from student s,teacher t where s.tid = t.id and t.id=#{id} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="name" column="tname"/> <collection property="students" ofType="Student"> <result property="id" column="sid" /> <result property="name" column="sname" /> <result property="tid" column="tid" /> </collection> </resultMap> </mapper>3、将Mapper文件注册到MyBatis-config文件中<mappers> <mapper resource="mapper/TeacherMapper.xml"/> </mappers>4、测试@Test public void testGetTeacher(){ SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); }10.3 按查询嵌套处理1、TeacherMapper接口编写方法public Teacher getTeacher2(int id);2、编写接口对应的Mapper配置文件<select id="getTeacher2" resultMap="TeacherStudent2"> select * from teacher where id = #{id} </select> <resultMap id="TeacherStudent2" type="Teacher"> <!--column是一对多的外键 , 写的是一的主键的列名--> <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/> </resultMap> <select id="getStudentByTeacherId" resultType="Student"> select * from student where tid = #{id} </select>3、将Mapper文件注册到MyBatis-config文件中4、测试@Test public void testGetTeacher2(){ SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher2(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); }小结1、关联-association2、集合-collection3、所以association是用于一对一和多对一,而collection是用于一对多的关系4、JavaType和ofType都是用来指定对象类型的JavaType是用来指定pojo中属性的类型ofType指定的是映射到list集合属性中pojo的类型。注意说明:1、保证SQL的可读性,尽量通俗易懂2、根据实际要求,尽量编写性能更高的SQL语句3、注意属性名和字段不一致的问题4、注意一对多和多对一 中:字段和属性对应的问题5、尽量使用Log4j,通过日志来查看自己的错误11.动态SQL11.1 介绍什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.官网描述: MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。 虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。 动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。 ------------------------------- - if - choose (when, otherwise) - trim (where, set) - foreach -------------------------------我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。11.2 搭建环境新建一个数据库表:blog字段:id,title,author,create_time,viewsCREATE TABLE `blog` ( `id` varchar(50) NOT NULL COMMENT '博客id', `title` varchar(100) NOT NULL COMMENT '博客标题', `author` varchar(30) NOT NULL COMMENT '博客作者', `create_time` datetime NOT NULL COMMENT '创建时间', `views` int(30) NOT NULL COMMENT '浏览量' ) ENGINE=InnoDB DEFAULT CHARSET=utf81、创建Mybatis基础工程2、IDutil工具类public class IDUtil { public static String genId(){ return UUID.randomUUID().toString().replaceAll("-",""); } }3、实体类编写 【注意set方法作用】import java.util.Date; public class Blog { private String id; private String title; private String author; private Date createTime; private int views; //set,get.... }4、编写Mapper接口及xml文件public interface BlogMapper { } <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mapper.BlogMapper"> </mapper>5、mybatis核心配置文件,下划线驼峰自动转换<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!--注册Mapper.xml--> <mappers> <mapper resource="mapper/BlogMapper.xml"/> </mappers>6、插入初始数据编写接口//新增一个博客 int addBlog(Blog blog);sql配置文件<insert id="addBlog" parameterType="blog"> insert into blog (id, title, author, create_time, views) values (#{id},#{title},#{author},#{createTime},#{views}); </insert>初始化博客方法@Test public void addInitBlog(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IDUtil.genId()); blog.setTitle("Mybatis如此简单"); blog.setAuthor("狂神说"); blog.setCreateTime(new Date()); blog.setViews(9999); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("Java如此简单"); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("Spring如此简单"); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("微服务如此简单"); mapper.addBlog(blog); session.close(); }初始化数据完毕!11.3 if 语句需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询1、编写接口类//需求1 List<Blog> queryBlogIf(Map map);2、编写SQL语句<!--需求1: 根据作者名字和博客名字来查询博客! 如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询 select * from blog where title = #{title} and author = #{author} --> <select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog where <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>3、测试@Test public void testQueryBlogIf(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("title","Mybatis如此简单"); map.put("author","狂神说"); List<Blog> blogs = mapper.queryBlogIf(map); System.out.println(blogs); session.close(); }这样写我们可以看到,如果 author 等于 null,那么查询语句为 select from user where title=#{title},但是如果title为空呢?那么查询语句为 select from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!11.4 Where修改上面的SQL语句;<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。11.5 Set同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?1、编写接口方法int updateBlog(Map map);2、sql配置文件<!--注意set是用的逗号隔开--> <update id="updateBlog" parameterType="map"> update blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id}; </update>3、测试@Test public void testUpdateBlog(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("title","动态SQL"); map.put("author","秦疆"); map.put("id","9d6a763f5e1347cebda43e2a32687a77"); mapper.updateBlog(map); session.close(); }11.6 choose语句有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句1、编写接口方法List<Blog> queryBlogChoose(Map map);2、sql配置文件<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and views = #{views} </otherwise> </choose> </where> </select>3、测试类@Test public void testQueryBlogChoose(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("title","Java如此简单"); map.put("author","狂神说"); map.put("views",9999); List<Blog> blogs = mapper.queryBlogChoose(map); System.out.println(blogs); session.close(); }11.7 SQL片段有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。提取SQL片段:<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>引用SQL片段:<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace --> <include refid="if-title-author"></include> <!-- 在这里还可以引用其他的 sql 片段 --> </where> </select>注意:①、最好基于 单表来定义 sql 片段,提高片段的可重用性②、在 sql 片段中不要包括 where11.8 Foreach将数据库中前三个数据的id修改为1,2,3;需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息1、编写接口List<Blog> queryBlogForeach(Map map);2、编写SQL语句<select id="queryBlogForeach" parameterType="map" resultType="blog"> select * from blog <where> <!-- collection:指定输入对象中的集合属性 item:每次遍历生成的对象 open:开始遍历时的拼接字符串 close:结束时拼接的字符串 separator:遍历对象之间需要拼接的字符串 select * from blog where 1=1 and (id=1 or id=2 or id=3) --> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>3、测试@Test public void testQueryBlogForeach(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap map = new HashMap(); List<Integer> ids = new ArrayList<Integer>(); ids.add(1); ids.add(2); ids.add(3); map.put("ids",ids); List<Blog> blogs = mapper.queryBlogForeach(map); System.out.println(blogs); session.close(); }小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。12.缓存12.1 简介1、什么是缓存 [ Cache ]?存在内存中的临时数据。将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。2、为什么使用缓存?减少和数据库的交互次数,减少系统开销,提高系统效率。3、什么样的数据能使用缓存?经常查询并且不经常改变的数据。12.2 Mybatis缓存MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)二级缓存需要手动开启和配置,他是基于namespace级别的缓存。为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存12.3 一级缓存一级缓存也叫本地缓存:与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;12.3.1 测试1、在mybatis中加入日志,方便测试结果2、编写接口方法//根据id查询用户 User queryUserById(@Param("id") int id);3、接口对应的Mapper文件<select id="queryUserById" resultType="user"> select * from user where id = #{id} </select>4、测试@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }5、结果分析12.3.2一级缓存失效的四种情况一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!1、sqlSession不同@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); session2.close(); }观察结果:发现发送了两条SQL语句!结论:每个sqlSession中的缓存相互独立2、sqlSession相同,查询条件不同@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper2.queryUserById(2); System.out.println(user2); System.out.println(user==user2); session.close(); }观察结果:发现发送了两条SQL语句!很正常的理解结论:当前缓存中,不存在这个数据3、sqlSession相同,两次查询之间执行了增删改操作!增加方法//修改用户 int updateUser(Map map);编写SQL<update id="updateUser" parameterType="map"> update user set name = #{name} where id = #{id} </update>测试@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); HashMap map = new HashMap(); map.put("name","kuangshen"); map.put("id",4); mapper.updateUser(map); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }观察结果:查询在中间执行了增删改操作后,重新执行了结论:因为增删改操作可能会对当前数据产生影响4、sqlSession相同,手动清除一级缓存@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.clearCache();//手动清除缓存 User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }一级缓存就是一个map12.4 二级缓存二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存基于namespace级别的缓存,一个名称空间,对应一个二级缓存;工作机制一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;新的会话查询信息,就可以从二级缓存中获取内容;不同的mapper查出的数据会放在自己对应的缓存(map)中;12.4.1 使用步骤1、开启全局缓存 【mybatis-config.xml】<setting name="cacheEnabled" value="true"/>2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】<cache/> 官方示例=====>查看官方文档 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。3、代码测试所有的实体类先实现序列化接口测试代码@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.close(); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session2.close(); }12.4.2 结论只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据查出的数据都会被默认先放在一级缓存中只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中12.5 缓存原理图12.6 第三方缓存-EhCache第三方缓存实现--EhCache: 查看百度百科Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;要在应用程序中使用Ehcache,需要引入依赖的jar包<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.1.0</version> </dependency>在mapper.xml中使用对应的缓存即可<mapper namespace = “org.acme.FooMapper” > <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> </mapper>编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!-- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下: user.home – 用户主目录 user.dir – 用户当前工作目录 java.io.tmpdir – 默认临时文件路径 --> <diskStore path="./tmpdir/Tmp_EhCache"/> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> <!-- defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。 --> <!-- name:缓存名称。 maxElementsInMemory:缓存最大数目 maxElementsOnDisk:硬盘最大缓存个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 overflowToDisk:是否保存到磁盘,当系统当机时 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 FIFO,first in first out,这个是大家最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> </ehcache>合理的使用缓存,可以让我们程序的性能大大提升!参考资料1.狂神说Java-Mybatis最新完整教程IDEA版通俗易懂
2022年03月06日
544 阅读
0 评论
0 点赞
2022-03-03
Nginx学习笔记
什么是Nginx?Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。2011年6月1日,nginx 1.0.4发布。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。在全球活跃的网站中有12.18%的使用比率,大约为2220万个网站。Nginx 是一个安装非常的简单、配置文件非常简洁(还能够支持perl语法)、Bug非常少的服务。Nginx 启动特别容易,并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。你还能够不间断服务的情况下进行软件版本的升级。Nginx代码完全用C语言从头写成。官方数据测试表明能够支持高达 50,000 个并发连接数的响应。Nginx作用?Http代理,反向代理:作为web服务器最常用的功能之一,尤其是反向代理。正向代理反向代理Nginx提供的负载均衡策略有2种:内置策略和扩展策略。内置策略为轮询,加权轮询,Ip hash。扩展策略,就天马行空,只有你想不到的没有他做不到的。轮询加权轮询iphash对客户端请求的ip进行hash操作,然后根据hash结果将同一个客户端ip的请求分发给同一台服务器进行处理,可以解决session不共享的问题。动静分离,在我们的软件开发中,有些请求是需要后台处理的,有些请求是不需要经过后台处理的(如:css、html、jpg、js等等文件),这些不需要经过后台处理的文件称为静态文件。让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作。提高资源响应的速度。目前,通过使用Nginx大大提高了我们网站的响应速度,优化了用户体验,让网站的健壮性更上一层楼!Nginx的安装windows下安装1、下载nginxhttp://nginx.org/en/download.html 下载稳定版本。以nginx/Windows-1.16.1为例,直接下载 nginx-1.16.1.zip。下载后解压,解压后如下:2、启动nginx有很多种方法启动nginx(1)直接双击nginx.exe,双击后一个黑色的弹窗一闪而过(2)打开cmd命令窗口,切换到nginx解压目录下,输入命令 nginx.exe ,回车即可3、检查nginx是否启动成功直接在浏览器地址栏输入网址 http://localhost:80 回车,出现以下页面说明启动成功!4、配置监听nginx的配置文件是conf目录下的nginx.conf,默认配置的nginx监听的端口为80,如果80端口被占用可以修改为未被占用的端口即可。当我们修改了nginx的配置文件nginx.conf 时,不需要关闭nginx后重新启动nginx,只需要执行命令 nginx -s reload 即可让改动生效5、关闭nginx如果使用cmd命令窗口启动nginx, 关闭cmd窗口是不能结束nginx进程的,可使用两种方法关闭nginx(1)输入nginx命令 nginx -s stop(快速停止nginx) 或 nginx -s quit(完整有序的停止nginx)(2)使用taskkill taskkill /f /t /im nginx.exetaskkill是用来终止进程的,/f是强制终止 ./t终止指定的进程和任何由此启动的子进程。/im示指定的进程名称 .linux下安装1、安装gcc安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,则需要安装:yum install gcc-c++2、PCRE pcre-devel 安装PCRE(Perl Compatible Regular Expressions) 是一个Perl库,包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析正则表达式,所以需要在 linux 上安装 pcre 库,pcre-devel 是使用 pcre 开发的一个二次开发库。nginx也需要此库。命令:yum install -y pcre pcre-devel3、zlib 安装zlib 库提供了很多种压缩和解压缩的方式, nginx 使用 zlib 对 http 包的内容进行 gzip ,所以需要在 Centos 上安装 zlib 库。yum install -y zlib zlib-devel4、OpenSSL 安装OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。nginx 不仅支持 http 协议,还支持 https(即在ssl协议上传输http),所以需要在 Centos 安装 OpenSSL 库。yum install -y openssl openssl-devel5、下载安装包手动下载.tar.gz安装包,地址:https://nginx.org/en/download.html下载完毕上传到服务器上 /root6、解压tar -zxvf nginx-1.18.0.tar.gzcd nginx-1.18.07、配置使用默认配置,在nginx根目录下执行./configure make make install查找安装路径: whereis nginxNginx常用命令cd /usr/local/nginx/sbin/ ./nginx 启动 ./nginx -s stop 停止 ./nginx -s quit 安全退出 ./nginx -s reload 重新加载配置文件 ps aux|grep nginx 查看nginx进程启动成功访问 服务器ip:80注意:如何连接不上,检查阿里云安全组是否开放端口,或者服务器防火墙是否开放端口!相关命令:# 开启 service firewalld start # 重启 service firewalld restart # 关闭 service firewalld stop # 查看防火墙规则 firewall-cmd --list-all # 查询端口是否开放 firewall-cmd --query-port=8080/tcp # 开放80端口 firewall-cmd --permanent --add-port=80/tcp # 移除端口 firewall-cmd --permanent --remove-port=8080/tcp #重启防火墙(修改配置后要重启防火墙) firewall-cmd --reload # 参数解释 1、firwall-cmd:是Linux提供的操作firewall的一个工具; 2、--permanent:表示设置为持久; 3、--add-port:标识添加的端口;参考资料狂神说-Nginx最新教程通俗易懂,40分钟搞定
2022年03月03日
835 阅读
1 评论
0 点赞
2022-03-03
Maven基础学习笔记
Maven基础学习笔记1. Maven简介1.1 Maven是什么Maven的本质是一个项目管理工具,将项目开发过程抽象成一个项目的对象模型(POM)POM(Project Object Model):项目对象模型1.2 Maven的作用项目构建:提供标准的、跨平台的自动化项目构建方式依赖管理:方便快捷的管理项目依赖的资源(jar包),避免资源间版本冲突问题统一开发结构:提供标准的、统一的项目结构2.下载与安装2.1 Maven下载官网:https://maven.apache.org/下载地址:https://maven.apache.org/download.cgi2.2 Maven安装Maven 属于绿色版软件,解压即安装2.3 Maven环境变量配置依赖java,需要配置JAVA_HOME设置Maven自身的环境变量,需要配置MAVEN_HOME并在Path下添加%MAVEN_HOME%\bin测试配置结果mvn3. Maven基础概念3.1 仓库仓库:用于存储资源,包含各种jar包仓库分类:本地仓库:自己电脑上存储资源的仓库,连接远程仓库获取资源远程仓库:非本机电脑的仓库,为本地仓库提供资源中央仓库:Maven团队维护,存储所有资源的仓库私服: 部门/公司范围内存储资源的仓库,从中央仓库获取资源私服的作用:保存具有版权的资源,包含购买或自主研发的jar包中央仓库的jar都是开源的,不能存储具有版权的资源一定范围内共享资源,仅对内部开放,不对外共享3.2 坐标什么是坐标?maven中的坐标用于描述资源的位置https://repo1.maven.org/maven2/Maven坐标组成groupId:定义当前Maven项目隶属于组织名称(通常是域名反写,例如:org.mybatis)artifactld:定义当前Maven项目名称(通常是模块名称,例如CRM\SMS)version:定义当前项目版本号packing:定义项目的打包方式示例:来自https://mvnrepository.com/<!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency>Maven坐标的作用使用唯一标识,唯一性定位资源位置,通过该标识可以将资源的识别与下载工作交给机器(maven工具)来完成4.仓库配置4.1 本地仓库配置修改本地仓库的位置默认位置:${user.home}/.m2/repository自定义位置1.新建好本地文件夹2.修改%MAVEN_HOME%/conf/bin/settings.xml<!-- localRepository | The path to the local repository maven will use to store artifacts. | | Default: ${user.home}/.m2/repository <localRepository>/path/to/local/repo</localRepository> --> # 在下方添加如下内容 <localRepository>/path/to/local/repo</localRepository>4.2 远程仓库配置镜像在setting文件中配置阿里云镜像仓库修改%MAVEN_HOME%/conf/bin/settings.xml<mirrors> # 在这里面添加 <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors>5. 第一个Maven项目(手工制作)5.1 Maven工程目录结构在src同层目录下创建pom.xml文件<!-- 属性 --> <properties> <spring.version>4.2.4.RELEASE</spring.version> <hibernate.version>5.0.7.Final</hibernate.version> <struts.version>2.3.24</struts.version> </properties> <!-- 锁定版本,struts2-2.3.24、spring4.2.4、hibernate5.0.7 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>${struts.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>${struts.version}</version> </dependency> </dependencies> </dependencyManagement> <!-- 依赖管理 --> <dependencies> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <!-- hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency> <!-- c3p0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- 导入 struts2 --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> </dependency> <!-- servlet jsp --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!-- 日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.2</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <plugins> <!-- 设置编译版本为1.7 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- maven内置 的tomcat6插件 --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <!-- 可以灵活配置工程路径 --> <path>/Hello</path> <!-- 可以灵活配置端口号 --> <port>8080</port> </configuration> </plugin> </plugins> </build>5.2 Maven项目构建命令Maven构建命令使用mvn开头,后面添加功能参数,可以一次执行多个命令,使用空格分隔mvn compile #编译 mvn clean #清理 mvn test #测试 mvn package #打包 mvn install #安装到本地仓库手动执行的话在pom.xml文件目录下执行6.第一个Maven项目(IDEA生成)https://www.bilibili.com/video/BV1Ah411S7ZE?p=9&spm_id_from=pageDriverjava项目web项目7.依赖管理7.1 添加依赖在pom.xml文件里的<dependencies></dependencies>中添加<dependency></dependency>7.2 依赖传递依赖具有传递性直接依赖:在当前项目中通过依赖配置建立的依赖关系间接依赖:被资源的资源如果依赖其他资源,当前项目间接依赖其他资源7.3 依赖传递冲突问题路径优先:当依赖中出现相同资源时,层级越深,优先级越低,层级越浅,优先级越高声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖顺序靠后的特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的(在同一个pom.xml文件中进行配置)7.4 可选依赖与排除依赖可选依赖控制资源不被调用者看到具体的依赖情况排除依赖指主动断开依赖的资源,被排除的资源无需要指定具体的版本--不需要7.5 依赖范围依赖的jar默认情况可以在任何地方使用,可以通过scope标签设定其作用范围作用范围主程序范围内有效(main文件夹范围内)测试程序范围有效(test文件夹范围内)是否参与打包(package指令范围内)依赖范围传递性(了解即可)带有依赖范围的资源在进行传递时,作用范围将受到影响8.生命周期与插件8.1 项目构建生命周期maven构建生命周期描述的是一次构建过程经历了多少个事件maven对项目构建的生命周期划分为3套clean:清理工作default:核心工作,例如编译、测试、打包、部署等site:产生报告,发布站点等8.2 插件插件与生命周期内的阶段绑定,在执行到对应生命周期时执行对应的插件功能默认maven在各个生命周期上绑定有预设的功能通过插件可以自定义其他功能参考资料黑马程序员Maven全套教程,maven项目管理从基础到高级
2022年03月03日
804 阅读
0 评论
0 点赞
1
...
11
12
13
...
24