最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

Java杂七杂八

IT圈 admin 2浏览 0评论

Java杂七杂八

理解原理,归纳总结,生成步骤,方便照搬

    • 万物皆字符串(SQL,前后端交互JSON,文件流等等)
    • 一个网站是怎么来的?
  • IDEA操作
      • IDEA常用操作
      • Git可视化操作(提交代码前先pull更新merge最新版本一下再push,保证提交的最终项目是最新)
    • IDEA中Git冲突的产生及解决方法
      • Git冲突的原因通常有以下几种:
    • Idea如何查看本地自动保存的代码版本
  • 实战注意(BUG总结)
    • DEBUG方法
      • Mybatis_Plus的debug步骤
      • 快速测试本地方法
      • idea断点调试
    • 如何查找前端页面调用的后台接口
    • 开发完后测试前后端交互响应速度
    • 内存问题
    • 空指针问题
    • Git的pull项目后与本地IDEA的JDK不一致以及maven错启默认配置问题
    • Mybaits-Plus的SQL ERR报错
    • Every derived table must have its own alias(sql语句错误解决方法)
    • limit注意事项
    • 空格和传参问题(postman)
    • 优化慢SQL语句
    • 时空准则(程序优化准则)
    • 后端封装post请求时400状态码报错(后端转码与浏览器转码bug)
    • Mybatis的select结果集包含多个字段值的封装方式
    • [Java 移除List中的元素,这玩意讲究!]()
    • Mybatis注解版SQL注意事项
    • 万能数据结构:JSONArray=List\<Object>和JSONObject=Map<String,Object>
  • JAVA
    • Java中,String类型和包装类型作为参数传递时,是属于值传递还是引用传递呢?
    • Java语法
    • java默认访问权限
    • String的substring
    • 中文乱码问题
    • 单例模式
  • DateUtil工具类(Date,Calender,SimpleDateFormat)
    • 计算日期之间差值(年月日时分秒毫秒)
      • 前情提要
      • Java代码(基于SimpleDateFormat,计算任意符合日期格式的变量类型与当前时间差)
    • Date日期转化为Long类型
    • 获取当前Date日期的不同YMD格式String
    • Calender实现日期变化操作
      • Calender得到上月末后几天日期
      • Calender获得月份天数
    • Calender获取上月月份(YYYYMM)
  • StrUtil工具类(StringBuffer)
    • 字符串拼接方法
  • File文件操作
    • JAVA - 按行读取文件
    • JAVA - 将数据生成为TXT文件(PrintWriter)
      • JAVA流操作结束后为什么要关闭流
    • JAVA-文件删除
      • java file 跨盘符_File类——遍历盘符根目录查找文件报错 java.lang.NullPointerException.
  • SpringBoot
    • SpringBoot注解
      • 启动类位置注意事项(一定要注意文件结构)
      • @MapperScan
    • SpringBoot+MybatisPlus的MVC模板
      • 注解版SQL样例
    • SpringBoot定时任务
    • slf4j的简单用法以及与log4j的区别
      • try-catch配合log.error食用
  • Mybatis-Plus
    • Mybatis-Plus的自定义SQL操作(注解版)
      • 手动切换数据源(JdbcTemplate+DriverManagerDataSource)
      • mapper层固定[crud教程](.html)
        • ${}和#{}
        • 注解
        • 注解版SQL样例
        • 条件判断更新
        • 批量操作
    • service层
    • serviceImpl层(代码逻辑实现层)
    • controller层(HTTP请求可以通过URL传参、请求头传参和请求体传参三种方式来传递参数。)
  • Linux环境
    • 程序执行操作系统命令、脚本、可执行文件并实现交互操作
  • 实战总结
    • 乐观锁处理多线程“抢占”现象
    • 更新的三种方式

万物皆字符串(SQL,前后端交互JSON,文件流等等)

一个网站是怎么来的?

IDEA操作

IDEA常用操作

IDEA常用快捷键
CTR+ALT+L:快速格式化代码。
CTR+左击:类名/方法名则直接进入对应类和方法。

右上角放大镜搜索,可以设定搜索范围


IDEA查看并修改编解码方式

Git可视化操作(提交代码前先pull更新merge最新版本一下再push,保证提交的最终项目是最新)

注意:pull的次数不要太多,防止将别人测试版pull下来,push的时候和正式版产生冲突。
尽量初始化项目pull一次,提交之前pull一次,最后push。
若是产生冲突就需要新开窗口pull下来最新的cv上自己的然后push。
pull后融合,改动的地方以工作区为主,其余地方以远程仓库为主,若本地和远程相差过大则冲突
IDEA的git操作教程
pull下来远程仓库
方法一

此处若是未配置用户名和密码会弹出输入框

方法二
或者直接用上述办法一直接pull下来对应的远程仓库,这样会直接配置连接上对应的远程仓库。(若未输入用户名密码则会弹出来对应输入框)
配置连接远程数据库


选中项目然后右键

方法三


log:分支状态可视化
git log:查看当前(HEAD指向)分支的所有(empty到HEAD)版本


检查一遍

最后push


方法三

最终若是push出问题直接force push

IDEA中Git冲突的产生及解决方法

IDEA中Git冲突的产生及解决方法
冲突产生的根本原因是:多方改动后的同一个文件的同一块区域的内容不同(行列数定位)。
单一账号改动的同一区域内容不会冲突。
不同账号改动的同一区域同一行会冲突。
Git问题:1.push时候遇到错误,push失败
究其原因是为了保证head指向的代码同步为最新版本。

push失败的情况,是因为我们在push提交代码的时候,远程仓库已经发生变化了(远程head头指针与本地保存的远程head头指针指向不同),换句话说就是在这个期间(上一次拉取代码到本次提交代码),有其他人在我们之前提交了代码到我们想要推送的分支,导致远程仓库代码更新变化了。所以git拒绝了本次push。

Git冲突的原因通常有以下几种:

同时修改了同一行代码:当两个人同时修改了同一行代码时,Git无法判断哪个修改是正确的,因此会产生冲突。

修改了同一文件的不同部分:当两个人修改了同一文件的不同部分时,Git会尝试合并这些修改,但如果这些修改之间存在冲突(修改同一行),就会产生冲突。

合并分支时:当合并两个分支时,如果这两个分支都修改了同一文件的同一行,就会产生冲突。

当Git发现冲突时,会在冲突的文件中标记出冲突的部分,并在文件中添加特殊的标记,如"<<<<<<<
HEAD"和"=======“和”>>>>>>>",以表示冲突的部分。此时需要手动解决冲突,即选择哪个修改是正确的,然后将特殊标记删除,保存文件并提交修改。

Idea如何查看本地自动保存的代码版本


恢复历史数据

实战注意(BUG总结)

问题解决!关键是解决问题的思路,由易到难,有外到内,确保最基本的配置不出问题。
层级调用,注意不要在触发层存在Dao层代码。
定时任务整理数据入表,接口触发提取定时任务整理的数据表,缩短响应时间。

DEBUG方法

Mybatis_Plus的debug步骤

若是dao层SQL报错则从console复制传参SQL,到对应数据库跑跑试试看看具体问题
查看mp提供的拼接后的SQL,若排除SQL语法错误而是SQL不完整的原因报错则必因为service层传参值为空导致的SQL拼接报错。
例如:insert拼接不全SQL报错则是因为传参为空,导致values后面无值。

排除过程

  • 仔细检查 map文件 和数据库表字段没有错误;
  • 将生产的SQL,贴到Mysql Client端执行;
  • 再次检查JDBC驱动链接URL;

快速测试本地方法

@test和类中main方法

Java每个类可以有也可以没有main方法, 甚至所有类里可以都没有main方法。
如果你想从某个类做为入口开始运行整个程序。那么就把他设成 public ,之后再里面写个main方法作为入口。
每个项目都要有一个public主类,这个主类中必须得有main,用于程序的入口.在程序测试时,一般每个类中都有一个main,用于方便测试人员对类成员进行测试
不是,可有可无。但是你要执行的类中必须有,因为main函数是提供程序执行的进入口。比如你 java Test 那么Test类中必须有个main函数。也可拿来做测试某个类用。如你要测试一下Test类内的方法(这时的Test类可能不是主类,假设这时此类只提供给主类一些功能),那么你可以在Test类中加入一个main方法,调用Test类中的成员变量和方法,查看结果,达到测试的效果
————————————————
版权声明:本文为CSDN博主「小熊coder」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:

idea断点调试

如何查找前端页面调用的后台接口

通过F12进入浏览器开发者模式,然后进入network选项。

找到你想要知道接口的按钮,点击它,会出现如下图所示标红的区域,就是你访问接口的方法名。

箭头所指向的就是你想要访问的后台接口的地址。
也存在此方法的 请求方式 和 状态码 等等…

开发完后测试前后端交互响应速度

尽量减少数据库链接次数,链接数据库CRUD数据花费的时间很多(不要在for和while中出现DAO层操作)。
定时任务整理数据入表,接口触发提取定时任务整理的数据表,缩短响应时间。

内存问题

程序中所有定义的变量都会占内存,注意若是批量插入list等需要暂存大量数据的操作考虑一下会不会爆内存(达到数据量后需要先插入然后list.clear一下)。
注意:记得最后list数组内可能剩余未达到数量标准的数据,判一下list.size()>0然后循环外再插入。
java8解惑之字符串常量池(实现原理、垃圾回收)
结论:因为常量池是放在堆中,所以他的回收和普通对象是一样的,没有其他特别的地方。

空指针问题

String判空方式
用list数组接收SQL返回的数据后,调用前先判断一下list是否为空,防止SQL未查询到数据。
如何优雅的判空
CollectionUtils.isEmpty()作用:判断参数null或者其size0
关于CollectionUtils.isEmpty()
注意:“”内容为空但非空对象可用isEmpty判断,但是null只能用CollectionUtils.isEmpty

if (CollectionUtils.isEmpty(regionCountList)) {logger.info("{}日增量数据为空", date);}

例如:

实体类中包装类初始化为null然后和数值或其他相加等操作导致空指针报错

Git的pull项目后与本地IDEA的JDK不一致以及maven错启默认配置问题

JDK修改为一致1.8版本。


maven改为自定义环境(setting以及仓库)

Mybaits-Plus的SQL ERR报错

查看mp提供的拼接后的SQL,若排除SQL语法错误而是SQL不完整的原因报错则必因为service层传参值为空导致的SQL拼接报错。
例如:每次insert前都先判断一下是否为空。

Every derived table must have its own alias(sql语句错误解决方法)

Every derived table must have its own alias(sql语句错误解决方法)
当SQL中用另一个select的结果集作为from表时,必须给结果集起别名。

limit注意事项

SQL中limit的用法
limit子句用于限制查询结果返回的数量,常用于分页查询
格式

select * from tableName limit i,n
# tableName:表名
# i:为查询结果的索引值(默认从0开始),当i=0时可省略i
# n:为查询结果返回的数量
# i与n之间使用英文逗号","隔开
limit n 等同于 limit 0,n

例子

# 查询10条数据,索引从0到9,第1条记录到第10条记录
select * from t_user limit 10;
select * from t_user limit 0,10;# 查询8条数据,索引从5到12,第6条记录到第13条记录
select * from t_user limit 5,8;

空格和传参问题(postman)

空格
普通文本格式中的空格,复制到SQL文本窗口或者程序开发/测试文本窗口,可能会出问题。
所以若是持续出现格式报错则删除重写。
postman的body中粘贴json注意空格问题:复制的json需要逐一替换空格
传参
Json串的key:value中key值不能首字母大写,容易和Date,Long等java类冲突。

优化慢SQL语句

写完select语句在生产库执行一下,看一下执行时间,时间过长的话需要优化。
group by 可以节省select查询时间。
恶意order by导致数据库慢查询(除非有索引)

时空准则(程序优化准则)

空间换时间还是时间换空间
定位问题:需要空间则用时间换,需要时间则用空间换。
数据表索引(时空准则案例)

在SQL中,索引可以加快查询速度,因为它们允许数据库引擎更快地查找数据。索引是一种数据结构,它可以帮助数据库引擎快速定位表中的数据。当你在查询中使用索引列时,数据库引擎会使用索引来查找数据,而不是扫描整个表。这样可以大大减少查询的数据量,从而提高查询速度。你可以使用CREATE
INDEX语句来创建索引。例如,以下是一个使用CREATE INDEX语句创建索引的示例:

CREATE INDEX index_name ON table_name (column_name);

在这个示例中,我们使用CREATE
INDEX语句在table_name表的column_name列上创建了一个名为index_name的索引。

后端封装post请求时400状态码报错(后端转码与浏览器转码bug)

Server returned HTTP response code: 400 for URL

400是一种HTTP状态码,告诉客户端它发送了一条异常请求。400页面是当用户在打开网页时,返回给用户界面带有400提示符的页面。其含义是你访问的页面域名不存在或者请求错误。主要分为两种。
1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。
2、请求参数有误

  • 浏览器能访问但是后端封装post却不能的问题原因

这个问题算是后端转码与浏览器转码的一个坑吧 。
后端默认转码问题,手动将参数转码为utf-8就把汉字问题解决了。

  • url携带中文

将url中,中文部分进行转码

URLEncoder.encode(url, "utf-8");
  • url携带空格

URLEncoder 空格会被编码+,而URI中空格为%20

String encodeUrl = URLEncoder.encode(url, "utf-8");
encodeUrl = encodeUrl.replaceAll("\\+", "%20");

程序
访问前的URL:xxx?name=姓名==>结果访问失败

修改后的URL:xxx?name=URLEncoder.encode(“姓名”,“utf8”)==>访问成功
也可以直接将全部参数都utf-8编码,因为英文用任何编码都和ascall编码一样。
总结
如果程序访问一个url出现server returned HTTP Response code :400 fro URL这个错误,但是在浏览器中访问同样的url没问题的话,就要考虑是不是因为访问的url中有特殊字符。如空格、逗号、中文等。把这些特殊字符进行url编码后在使用程序进行访问或许就能成功了。需要注意,在进行url编码的时候,指定编码的utf-8字符集。

Mybatis的select结果集包含多个字段值的封装方式

方式一:实体类映射对应返回结果集多个字段并封装。
方式二:List<Map<String,Object>>
java.math.BigDecimal cannot be cast to java.lang.String
String封装字段名,Object封装字段值(默认为decimal类型):Object.toString()提取字段值。
注意:每个map封装的是一行记录,字段名为key,字段值为value。

Java 移除List中的元素,这玩意讲究!

注意:不可以在Java的foreach循环中remove元素。

Mybatis注解版SQL注意事项

mybatisCause: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 75; 元素内容必须由格式正确的字符数据或标记组成。

  • 复制SQL时;普通文本格式中的空格,复制到SQL文本窗口或者程序开发/测试文本窗口,可能会出问题。
  • 不能使用重载,因为SQL是根据方法名绑定,所以不能使用重载方法。
  • 添加脚本时:使用<>标签前一定要用<script>声明本SQL包含脚本。
  • 大小比较时:Mybatis注解版SQL可以使用<和>但是不能使用<=或者>=需要用转义字符替换,需要使用<script>声明才能用转义字符。
    xml格式的不允许出现类似“>”这样的字符也需要转义。
    org.xml.sax.SAXParseException: 元素内容必须由格式正确的字符数据或标记组成。 的解决办法
  • @DS:在Dao层绑定不了数据库则需要在IMPL层再@DS绑定一下数据库。

万能数据结构:JSONArray=List<Object>和JSONObject=Map<String,Object>

JSONArray 是一个有序的、元素不唯一的 JSON 数组,其数据结构类似于 Java 中的 List 或 Python 中的 List。

它由一对中括号 [] 包围,中间用逗号分隔每个元素。每个元素可以是 JSON 的任意数据类型,包括:

JSONObject:JSON 对象
JSONArray:JSON 数组
String:字符串
Number:数字(整数或浮点数)
Boolean:布尔值
null:空值

例如,以下是一个包含不同数据类型的 JSONArray:

["apple", 123, true, null, {"name": "John", "age": 30}, [1, 2, 3]]

JSONObject 可以非常灵活地表示各种数据结构
JSONObject 是一种用于存储和处理 JSON 数据的 Java 对象。它可以表示一个 JSON 对象,即由键值对组成的无序集合。在 JSONObject 中,键是字符串,值可以是基本数据类型(如整数、浮点数、布尔值、字符串)或者是其他的 JSONObject 或 JSONArray 对象。
举个例子,下面是一个 JSONObject 的示例:

{"name": "Alice","age": 25,"isStudent": true,"hobbies": ["reading", "swimming", "travelling"],"address": {"city": "Beijing","country": "China"}
}

JAVA

Java中,String类型和包装类型作为参数传递时,是属于值传递还是引用传递呢?

包装类型属于引用传递但是传参后方法改变数值后不会影响原数值。
因为包装类的数值为final类型所以不可改变而是会直接新建一个包装类对象来接收新数值(可比较内存地址查看是否为新建对象)。
其余自定义对象的引用(指针:内存地址)传值则可以在实例方法中改变对象值,原值同时改变


Java中的参数传递:分为值传递和引用传递
但本质上,Java中只有值传递。引用传递,其实可以理解为传的是类似指针的东西。
值传递就是把基本变量的值拷贝一份,传递这个拷贝。引用传递则是传递的引用的地址,也就是该变量在内存空间的地址
值传递
只有基本数据类型采用值传递,特点是传递的是值的拷贝,传递完后两者就没有关系了。也就是说方法内和方法外的值互不相干
基本数据类型:

  • 整型:int,long,byte,short
  • 浮点型:float,double
  • 字符型:char
  • 布尔型:boolean
    注:8种基本数据类型以外的数据类型都为引用类型。

引用传递
指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。
传递的是一个拷贝,即副本。也就是说,对于一个参数传递,存在两个地址指向同一个内存空间。这里我们可以用内存分配示意图来体现
String类型传递
先说结论,String类型传递与基本数据类型的传递效果相似。
说明:
String类对象一旦创建,其内容不可更改:
String类的所有方法都不会改变String类对象内容,要改变String类对象的值就必须创建一个新的String对象。
也就是说,当进行参数传递时,如果方法内对String类对象的值进行了修改,那么实际上是创建了一个新的String类对象,然后让原来的变量指向它而已。但是这个“原来的变量”是一份拷贝副本,只是一开始创建的时候与主方法中的传递的值相同而已,现在改变之后,两者就毫无关系了。

Java语法

java语法记录
java对象new实例之后,实例bean对应的变量也会赋初值(数值类型0或对象类型null)。

String初始值:null
Double初始值:null
Long初始值:null
double初始值:0.0
long初始值:0

java默认访问权限

Java默认访问权限

String的substring

注意:substring(start,end)中索引不能越界,否则报错。
java.lang.StringIndexOutOfBoundsException: String index out of range

中文乱码问题

对unicode的编码与解码方式不一致。
一听就能懂字符集、ASCII、GBK、Unicode、UTF-8、字符编码、解码、乱码问题的讲解

单例模式

工具类
构造方法private
全都是静态方法
类名.静态方法(参数)实现功能

DateUtil工具类(Date,Calender,SimpleDateFormat)

Java获取当前时间的上一年、下一年、上个月、下个月、前一天等(时间格式化)

计算日期之间差值(年月日时分秒毫秒)

注意:String可以作为任意类型转化为Date类型的中介
Object.toString()---->String–SimpleDateFormat.parse–>Date
Date–SimpleDateFormat.format–>String---->Object.parseObject(String)

前情提要

format时间格式
可带前导零的精确到毫秒的时间格式:yyyy-MM-dd HH:mm:ss.SSS:2022-09-28 16:19:08.338

注意:时间格式区分大小写
不要把月份的MM写为分钟mm
java中的的日期格式为:

yyyy-MM-dd HH:mm:ss:代表将时间转换为24小时制,例: 2020-01-07 13:21:55
yyyy-MM-dd hh:mm:ss: 代表将时间转换为12小时制,例: 2020-01-07 03:24:21

oracle中( to_char())的日期格式为(不区分大小写):
oracle日期格式转换 to_date(),to_char()
oracle 日期格式
(1)to_date(“要转换的字符串”,“转换的格式”) 两个参数的格式必须匹配,否则会报错。
是将字符串转化为日期(DATE)格式,而且转化之后的格式与orcal系统日期参数有关,
(2)to_char(日期,“转换格式” ) 即把给定的日期按照“转换格式”转换。
是将日期格式转化为字符串格式
Qracle中不区分大小写,MM和mm被认为是相同的格式代码,所以用mi替代mm

yyyy-MM-dd HH24:mi:ss:代表oracle中的24小时制,例:2020/1/7 13:21:55
yyyy-MM-dd HH:mi:ss: 代表oracle中的12小时制,例:2020/1/7 9:21:55

之所以 oracle和java不同,是因为我们知道oracle是不区分大小写的,所以java中根据大小写来代表24小时和12小时的表达式在oracle中就会出问题,oracle中将24小时的时和分做了特殊处理.如上所示,在hh后面加上了24,将mm改为了mi.
Java:

注意
Oracle数据库,今天根据时间日期查询展示数据,SQL语句是:
SELECT * FROM JCTJ_JXDB WHERE
DATAMONTH = TO_DATE(‘2021-11-29 15:12:54’,‘YYYY-MM-dd HH:mm:ss’),然后提示报错信息
SQL 错误 [1810] [22008]: ORA-01810: 格式代码出现两次
主要原因是由于Qracle中不区分大小写,MM和mm被认为是相同的格式代码,所以Oracle的SQL采用了mi代替分钟,如下修改为如下SQL即可:SELECT * FROM JCTJ_JXDB WHERE
DATAMONTH = TO_DATE(‘2021-11-29 15:12:54’,‘YYYY-MM-dd HH:mi:ss’),执行后,报如下错误提示:小时值必须介于1和12之间
这是由于我们使用的24小时制,如果我们的时间在0-12之间,是可以使用HH表示的,但现在我们查询的小时为15,大于12,所以在查询中需要使用HH24来表示小时

Java代码(基于SimpleDateFormat,计算任意符合日期格式的变量类型与当前时间差)

SimpleDateFormat
SimpleDateFormat格式化日期的方法和参数
SimpleDateFormat 报错:Unparseable date:String日期格式和format不匹配
String—SimpleDateFormat(format) —》Date
DateUtil工具类:

 		String lastTime="";String format="yyyy-MM-dd HH:mm:ss.SSS";//指定字符串的日期格式Long Day=DateUtil.currentDifferenceD(lastTime,format);if(Day==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");Long hour=DateUtil.currentDifferenceH(lastTime,format);if(hour==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");Long minute=DateUtil.currentDifferenceM(lastTime,format);if(minute==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");Long s=DateUtil.currentDifferenceS(lastTime,format);if(s==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");Long sss=DateUtil.currentDifferenceSSS(lastTime,format);if(sss==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");

工具类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class DateUtil {private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);private DateUtil(){}/*** 计算参数与当前时间的天数差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceD(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);//此方法仅可用于String类型转为Date类型,所以参数需要先转化为String类型Date date = formatter.parse(lastTime);Long nd=1000*60*60*24L;
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();diff=diff/nd;//计算差多少天return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}/*** 计算参数与当前时间的小时差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceH(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);Date date = formatter.parse(lastTime);Long nh=1000*60*60L;
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();diff=diff/nh;//计算差多少小时return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}/*** 计算参数与当前时间的分钟差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceM(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);Date date = formatter.parse(lastTime);Long nd=1000*60L;
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();diff=diff/nd;//计算差多少分钟return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}/*** 计算参数与当前时间的秒差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceS(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);Date date = formatter.parse(lastTime);Long nd=1000L;
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();diff=diff/nd;//计算差多少秒return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}/*** 计算参数与当前时间的毫秒差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceSSS(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);Date date = formatter.parse(lastTime);
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}}

Date日期转化为Long类型

注意:String可以作为任意类型转化为Date类型的中介。
Object.toString()---->String–SimpleDateFormat.parse–>Date
Date–SimpleDateFormat.format–>String---->Object.parseObject(String)
因为日期可能为YYYYMMDD八位或者毫秒数更多所以只能用Long来存储,防止越界。

between…and…:可用于字符串以及日期

    /*** 将Date类型转化为Long类型*/public static Long dateToLong(Date date,String format){SimpleDateFormat formatter= new SimpleDateFormat(format);String time=formatter.format(date);return Long.parseLong(time);//String类型转为Long类型}

获得当前YYYYMM减去三个月后的YYYYMM

        SimpleDateFormat formatter= new SimpleDateFormat("yyyyMMdd");Calendar cal = Calendar.getInstance();cal.add(Calendar.MONTH, -3);Date result = cal.getTime();String lastMonth=formatter.format(result);System.out.println(lastMonth);
        //用毫秒数初始化构建Date对象Date dateNew = new Date(System.currentTimeMillis());SimpleDateFormat formatN = new SimpleDateFormat("yyyyMM");//获取1970-01-01 00:00:00到此Date对象上时间的毫秒数(初始化构建该对象的毫秒数)==System.currentTimeMillis().Long date = dateNew.getTime();//减去三个月的毫秒数date = date - 93L * 24L * 60L * 60L * 1000L;//操作完Long后再次初始化构建Date对象dateNew = new Date(date);String delMonth = formatN.format(dateNew);

获取当前Date日期的不同YMD格式String

    /*** 获取当前日期YYYYMMDD格式*/public static String getDateYYYYMMDD(){//当前日期Date dateNew = new Date(System.currentTimeMillis());SimpleDateFormat formatN=new SimpleDateFormat("yyyyMMdd");return formatN.format(dateNew);}/*** 获取当前账期YYYYMM格式*/public static String getDateYYYYMM(){//当前日期Date dateNew = new Date(System.currentTimeMillis());SimpleDateFormat formatN=new SimpleDateFormat("yyyyMM");return formatN.format(dateNew);}

Calender实现日期变化操作

Calender得到上月末后几天日期

例如:
正常情况下自己判断月末后五天是什么日期则需要考虑月份是30天还是31天如果是二月还要考虑闰年29天和平年28天情况。
Calender实现了这个功能。

        public static String getStartTime(int prevDay){Calendar c = Calendar.getInstance();Date date = c.getTime();//获取当前日期SimpleDateFormat formatN=new SimpleDateFormat("yyyyMMdd");String day =formatN.format(date).substring(6);//获取当天日期的天数int preDay = Integer.parseInt(day) + prevDay - 1;//获取当前天数与上个月倒数第prevDay天的差值c.add(Calendar.DATE,-(preDay));//当前日期减去插值return formatN.format(c.getTime());}

得到传入月份的上月末后prevDay天日期
传入:20221011,1
返回:20220930

    public static String getLastStartTime(String lastTime,int prevDay) {try{SimpleDateFormat formatN = new SimpleDateFormat("yyyyMMdd");Date date =formatN.parse(lastTime);Calendar c = Calendar.getInstance();c.setTime(date);String day = formatN.format(date).substring(6);int preDay = Integer.parseInt(day) + prevDay - 1;c.add(Calendar.DATE, -(preDay));return formatN.format(c.getTime());}catch (ParseException e) {logger.error(e.getMessage());}return null;}

Calender获得月份天数

   /*** 传入时间 获得天数 如果要得到其他月份的天数 设置对象的月份以及年份即可* */public static int getMonthDay(String yyyymm) {Calendar a = Calendar.getInstance();//获取当前时间Integer year= Integer.valueOf(yyyymm.substring(0,4));Integer month=Integer.valueOf(yyyymm.substring(4));a.set(Calendar.YEAR, year);a.set(Calendar.MONTH, month - 1);// Calendar月份是以0开始的 所以要-1a.set(Calendar.DATE, 1);//把日期设置为当月第一天a.roll(Calendar.DATE, -1);//日期回滚一天,也就是最后一天int day = a.get(Calendar.DATE);return day;}

Calender获取上月月份(YYYYMM)

    /*** 获取上月月份*/public static String getLastMonth(){SimpleDateFormat format = new SimpleDateFormat("yyyyMM");Date date = new Date();Calendar calendar = Calendar.getInstance();// 设置为当前时间calendar.setTime(date);calendar.add(Calendar.MONTH,-1);// 设置为上一个月date = calendar.getTime();return format.format(date);}

StrUtil工具类(StringBuffer)

String类型传递
先说结论,String类型传递与基本数据类型的传递效果相似。
说明:
String类对象一旦创建,其内容不可更改:
String类的所有方法都不会改变String类对象内容,要改变String类对象的值就必须创建一个新的String对象。
也就是说,当进行参数传递时,如果方法内对String类对象的值进行了修改,那么实际上是创建了一个新的String类对象,然后让原来的变量指向它而已。但是这个“原来的变量”是一份拷贝副本,只是一开始创建的时候与主方法中的传递的值相同而已,现在改变之后,两者就毫无关系了。

字符串拼接方法

Java中方法的参数传递机制以及形参个数可变的方法

    /*以可变个数形参来定义方法,可传入任意个数String字符串参数String... strings*/public  static String strSplice(String... strings){StringBuffer stringBuffer=new StringBuffer();for(String s:strings){stringBuffer.append(s);}return stringBuffer.toString();}

File文件操作

JAVA - 按行读取文件

  /*** 文件按行解析*/public static Map<Integer, String> parseFile(String path, String fileName) {File file = new File(path + "/" + fileName);Map<Integer, String> lineData = new HashMap<>(16);try (BufferedReader reader = new BufferedReader(new FileReader(file));) {String tempString = null;int line = 0;while ((tempString = reader.readLine()) != null) {lineData.put(++line, tempString);}} catch (Exception e) {logger.error(e.getMessage());}return lineData;}

JAVA - 将数据生成为TXT文件(PrintWriter)

JAVA - 将数据生成为TXT文件(可追加append,可重写)

JSONArray用法
测试用例
注意:java中表示文件夹目录是类似如下格式(结尾是\\否则认定最后一层为文件名)
windows路径格式
String path=“D:\\Atemp\\…\\”;
linux路径格式
String path=“/home/xxx/data/…/”;

JSONArray jsonArray=new JSONArray();JSONObject obj = new JSONObject();JSONObject obj1 = new JSONObject();obj.put("orderId",456);obj.put("packageId", 789);obj.put("startTime", 789);obj1.put("orderId",456);obj1.put("packageId", 789);obj1.put("startTime", 789);jsonArray.add(obj);jsonArray.add(obj1);// 这是用于访问操作系统临时目录的属性名称// 或文件夹。String property = "java.io.tmpdir";// 获取临时目录并打印。String tempDir = System.getProperty(property);System.out.println("OS temporary directory is " + tempDir);createTxtFile(jsonArray,tempDir,"AAA");
  public static boolean createTxtFile(JSONArray jsonArray, String path, String filename) {// 标记文件生成是否成功boolean flag = true;try {// 含文件名的全路径String[] strings = {path, filename, ".txt"};String fullPath = StrUtils.strSplice(strings);File file = new File(fullPath);File folder = new File(path);if (!folder.exists() && !folder.isDirectory()) {// 如果不存在,创建文件夹folder.mkdirs();}if (!file.exists()) {file.createNewFile();}// 格式化浮点数据NumberFormat formatter = NumberFormat.getNumberInstance();// 设置最大小数位为10formatter.setMaximumFractionDigits(10);// 格式化日期数据SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");// 遍历输出每行PrintWriter pfp = new PrintWriter(new FileOutputStream(file, true));for (int i = 0; i < jsonArray.size(); i++) {JSONObject jsonObject = jsonArray.getJSONObject(i);if (jsonObject.isEmpty()) {break;}StringBuilder thisLine = new StringBuilder("");for (Iterator<String> iterator = jsonObject.keySet().iterator(); iterator.hasNext(); ) {// 当前字段String key = iterator.next();Object obj = jsonObject.get(key);// 格式化数据String field = "";if (null != obj) {if (obj.getClass() == String.class) {// 如果是字符串field = (String) obj;} else if (obj.getClass() == Double.class || obj.getClass() == Float.class) {// 格式化浮点数,使浮点数不以科学计数法输出field = formatter.format(obj);} else if (obj.getClass() == Integer.class || obj.getClass() == Long.class|| obj.getClass() == Short.class || obj.getClass() == Byte.class) { // 如果是整形field += obj;} else if (obj.getClass() == Date.class) {// 如果是日期类型field = sdf.format(obj);}} else {// null时给一个空格占位field = " ";}// 拼接所有字段为一行数据,用逗号分隔// 不是最后一个元素if (iterator.hasNext()) {thisLine.append(field).append(",");} else {// 是最后一个元素thisLine.append(field);}}pfp.print(thisLine.toString() + "\n");}pfp.close();} catch (Exception e) {flag = false;log.error("生成txt文件失败", e);}return flag;}

JAVA流操作结束后为什么要关闭流

需要自己close的东西,一般都是用了虚拟机之外的资源,例如端口,显存,文件等,虚拟机无法通过垃圾回收释放这些资源,只能你显式调用close方法来释放。

许多情况下,如果在一些比较频繁的操作中,不对流进行关闭,很容易出现输入输出流经超越了JVM的边界,所以有时可能无法回收资源。
所以流操作的时候凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。

你读写一个文件,忘记关闭了流,你在操作系统里对这个文件的写,删除等操作就会报错,告诉你这个文件被某个进程占用

JAVA-文件删除

此方法是遍历给定file目录下的全部文件夹以及文件查找对应fileName文件来删除。

public class Demo {public static void main(String[] args) {//创建File对象,给定磁盘目录File file = new File("D:\\work");//设定待删除文件的名称String fileName = "xxx.txt";//把file对象传递给方法getFiledeleteFile(file,fileName);}public static void deleteFile(File file, String fileName) {if (file.exists()) {//获取file类的所有文件和目录File[] files = file.listFiles();if (files != null) {for (File f : files) {//判断是否为目录,如果为目录则利用递归继续进行寻找文件if (f.isDirectory()) {deleteFile(f, fileName);}//不是目录,那就是文件,则判断文件后缀是否为.txt,如果是则删除if (f.getName().equals(fileName)) {System.out.println(f.getName());f.delete();//log.infor(fileName + "文件删除完毕!")System.out.println(fileName + "文件删除完毕!");}}}}}

java file 跨盘符_File类——遍历盘符根目录查找文件报错 java.lang.NullPointerException.

注意:一定要判断file.exists并且files!=null才可以继续执行。
因为遍历到System Volume Information目录时,用户没有访问权限,所以调用file.listFile方法会返回null给files数组而不是file目录下的全部文件,所以循环体中数组元素调用方法的时候会产生空指针异常。

System Volume Information”是windows系统文件夹

是隐藏文件

中文名称可以翻译为“系统卷标信息”

这个文件夹里就存储着系统还原的备份信息

System Volume Information 文件夹是一个隐藏的系统文件夹

"系统还原"工具使用该文件夹来存储它的信息和还原点

例如:

SpringBoot

SpringBoot注解

Spring全家桶回顾

启动类位置注意事项(一定要注意文件结构)

SpringBoot启动类配置@Scan扫描包注解后,对应basePackages = "XXX.xxx"包下的类或接口等就不需要再添加对应@注解了。
 

AppApplication 一定要在包的最外层,否则Spring无法对所有的类进行托管,会造成@Autowired 无法注入。
这是因为SpringBoot项目的注解Bean装配默认规则是根据AppApplication 类所在的包位置从上往下扫描!即只会扫描AppApplication 所在的包及其子包,其他包路径不会被扫描!!!


若想扫描其他包中配置类则需要配置@ComponentScan。

@MapperScan

作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类。
配置扫描的是java接口不是xml文件,而maven默认不扫描xml文件,需要单独配置maven扫描xml文件。
@MapperScan注解

———————————————— 版权声明:本文为CSDN博主「哈6哈6」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:

SpringBoot+MybatisPlus的MVC模板

Mybatis积累
注解版Mapper省去了绑定xml和Mapper类的步骤,直接将SQL以及数据库和Mapper方法绑定在一起。

注解版SQL样例

script插入脚本声明
<script><foreach collection=\"updateUsers\" item=\"updateUser\" separator=\";或,\"><if test=''></if></foreach>
</script>

注意:foreach中分割符separator的选定,若是多个sql拼接则需要用;作为分割符,若是多个字段拼接则需要用,作为分割符。
foreach标签的分隔符可以使用separator属性指定,默认为逗号。如果需要使用其他分隔符,可以在separator属性中指定。

@Update("<script> " +"<foreach collection=\"updateUsers\" item=\"updateUser\" separator=\";\">" +" UPDATE" +" overview_audit_delay " +"<set>" +"<if test='updateUser.flage!=null'>" +"flage=#{updateUser.flage}, " +"</if>" +"<if test='updateUser.remarks!=null'>" +"remarks=#{updateUser.remarks}," +"</if>" +"update_time=#{updateUser.updateTime}" +"</set>" +" WHERE " +" user_id = #{updateUser.userId} and cycle_month = #{updateUser.cycleMonth} and deleted=0"+"</foreach>" +"</script>")@DS("objfzxserv")void updateRepAndRefUsers(@Param("updateUsers") List<AbnormalDelayUser> updateUsers);

SpringBoot定时任务

定时任务就是定时触发service方法。
SpringBoot定时任务
启动类添加注解:@EnableScheduling,定时类添加注解:@Component,定时方法添加注解@Scheduled(cron=“cron表达式”或fixed注解)
@EnableScheduling //可以在启动类上注解也可以在执行调度任务类

@Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行
@Scheduled(fixedDelay= 5000) :上一次执行完毕时间点之后5秒再执行
@Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
@Scheduled(cron="0*/1 * * * ? ") :通过cron表达式定义规则,表示每隔1分钟执行一次

slf4j的简单用法以及与log4j的区别

log的最大好处是可以在docker容器的终端环境中查看对应记录。
slf4j的简单用法以及与log4j的区别

package cn.xm.exam.test;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jTest {private static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);// slf4j日志记录器public static void main(String[] args) {// 普通的日志记录logger.debug("普通的日志记录");// {}占位符记录日志for (int i = 0; i < 3; i++) {logger.debug("这是第{}条记录", i);}// 用\转义{}logger.debug("Set \\{} differs from {}", "3"); // output:Set {} differs// from 3// 两个参数logger.debug("两个占位符,可以传两个参数{}----{}", 1, 2);// 多个参数(可变参数)logger.debug("debug:多个占位符,{},{},{},{}", 1, 2, 3, 4);// 多个参数(可变参数)logger.info("info:多个占位符,{},{},{},{}", 1, 2, 3, 4);// 多个参数(可变参数)logger.error("error:多个占位符,{},{},{},{}", 1, 2, 3, 4);}}

@Slf4j是用作日志输出的,一般会在项目每个类的开头加入该注解,如果不写下面这段代码,并且想用log

private final Logger logger = LoggerFactory.getLogger(当前类名.class);

就可以用@Slf4来代替;这样就省去这段很长的代码。
默认log变量实现对应功能。


@Controller
@RequestMapping("/abc")
@Slf4j
public class QueryBillController {@Autowiredprivate TraceService traceService;@Autowiredprivate SignatureAndVerification signatureAndVerification;/*** 账单查询接口*/@RequestMapping(.........)@ResponseBodypublic String getBills(@RequestBody String queryRequest) {log.info("进入账单查询接口");

添加了该注释之后,就可以在代码中直接使用log.info( ) 等打印日志了

try-catch配合log.error食用

  try{//关闭数据库资源连接Connection conn = dataSource.getConnection();conn.close();}catch (Exception e){logger.error("数据库资源释放失败"+e.getMessage());}

Mybatis-Plus

写好SQL后先去数据库跑跑看看是否正确。

Mybatis-Plus的自定义SQL操作(注解版)

下图对应的Mapper层和service层以及serviceImpl层的类声明以及注解(@Mapper,@Service)都是MP规范格式模板。
实现了MP制定好的简单单表CRUD方法声明(Service)和方法实现(ServiceImpl基于VoMapper),只需要传参VoMapper和Vo即可.(若多个数据库则需要@DS指定进行自定义SQL操作,不可用封装方法)
interface接口都是声明crud方法,class才是基于实例化base/VoMapper等实现crud方法
继承复用了MP制定的规范格式模板,可以直接Mapper调用MP封装的CRUD方法实现功能

model层(实体类命名与数据字段名要协调)
原理:Vo实体类变量封装映射的是select的结果集,可以通过as给结果集起别名来匹配Vo的变量名。
mybatis的sql中字段两种映射(映射到实体)方式

@TableField(select = false)    //查询时,则不返回该字段的值
@TableField(value = "字段名")    //通过tableField进行字段名和变量名不一致的映射
@TableField(exist = false)  //声明该字段在数据库表中不存在,不参与数据库表的映射
@Data
public class Vo{
private Long 对应数据表 bigInt
private String 对应数据表 varchar
}

手动切换数据源(JdbcTemplate+DriverManagerDataSource)

该方法适应于需要根据传参动态切换数据源的程序。
若是仅仅想用JdbcTemplate提升效率则可以配合@DS食用。

JdbcTemplate是Spring Framework中一个核心的JDBC模板类,它简化了JDBC的开发流程,封装了JDBC的重复代码,使得开发者能够更加方便快捷地进行数据库访问。

使用JdbcTemplate,需要创建一个JdbcTemplate对象,可以通过构造函数注入数据源对象(如:DriverManagerDataSource、C3P0DataSource、BoneCPDataSource等)或者通过setDataSource()方法注入数据源对象。之后,就可以通过该JdbcTemplate对象执行SQL语句,比如:查询数据、插入数据、更新数据、删除数据等。

//手动设置数据源DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/test");dataSource.setUsername("root");dataSource.setPassword("root");
// 通过构造函数传入数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 或者通过setDataSource方法设置数据源
jdbcTemplate.setDataSource(dataSource);jdbcTemplate.增删改查//用完之后需要关闭DriverManagerDataSource连接try{//关闭数据库资源连接Connection conn = dataSource.getConnection();conn.close();}catch (Exception e){logger.error("数据库资源释放失败"+e.getMessage());}
 - 查询单个值
String sql = "SELECT COUNT(*) FROM users";
int count = jdbcTemplate.queryForObject(sql, Integer.class);- 查询单个对象
String sql = "SELECT * FROM users WHERE id=?";
User user = jdbcTemplate.queryForObject(sql, new Object[] { id }, new BeanPropertyRowMapper<>(User.class));- 查询多个对象
String sql = "SELECT * FROM users";
List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));- 带参数查询多个对象
String sql = "SELECT * FROM users WHERE age > ?";
List<User> users = jdbcTemplate.query(sql, new Object[] { age }, new BeanPropertyRowMapper<>(User.class));- 查询多个对象,并分页
String sql = "SELECT * FROM users";
int pageNumber = 1;
int pageSize = 10;
int startIndex = (pageNumber - 1) * pageSize;
List<User> users = jdbcTemplate.query(sql + " LIMIT ?, ?", new Object[] { startIndex, pageSize }, new BeanPropertyRowMapper<>(User.class));- 查询多个对象,并按字段排序
String sql = "SELECT * FROM users ORDER BY age DESC";
List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));- 执行查询并处理结果集
String sql = "SELECT * FROM users";
jdbcTemplate.query(sql, rs -> {while (rs.next()) {int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");// 处理结果集}
});- 执行查询,并自定义结果集处理器
String sql = "SELECT * FROM users";
jdbcTemplate.query(sql, new MyRowMapper());
class MyRowMapper implements RowMapper<User> {@Overridepublic User mapRow(ResultSet rs, int rowNum) throws SQLException {int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");// 处理结果集return new User(id, name, age);}
}- 执行查询返回List<Map<String, Object>>
public List<Map<String, Object>> queryData() {return jdbcTemplate.queryForList("SELECT * FROM user");
}- 执行查询返回Map<String, Object>
public Map<String, Object> queryData() {return jdbcTemplate.queryForList("SELECT * FROM user limit 1");
}

mapper层固定crud教程

自定义Vomapper继承baseMapper的作用主要是基于baseMapper简单单表crud的基础上,添加自定义crud方法。

${}和#{}

mybatis中的#和$的区别
#{} 和 ${} 的区别 — 详细!
大多数情况下还是经常使用#,一般能用#的就别用$;但有些情况下必须使用$,传入表名或字段名的时候,例:MyBatis排序时使用order by 动态参数时需要注意,用$而不是#
#传入的参数在SQL中显示为字符串,$传入的参数在SQL中直接显示为传入的值.

#不需要加’‘,本身自带’',$需要按照情况分析。

效果上: #{}=‘${}’=‘参数’

SQL中字符串可以直接比较,并且其它类型也可以直接转为字符串比较。

where id = 66和where id = '66'等效

注解

@Param
首先明确这个注解是为SQL语句中参数赋值而服务的。

  @Param的作用就是给参数命名,比如在mapper里面某方法A(int id),当添加注解后A(@Param("userId") int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传如SQL语句中,通过#{userId}进行取值给SQL的参数赋值。


或者spring.datasource.custom.name 配置默认数据源

@Mapper
@DS("该类全部SQL方法调用的数据库")
//若整个项目仅有一个数据库则忽略该注解,若该注解加在方法上则是表示仅限该方法调用的数据库
public interface  VoMapper extends BaseMapper<Vo> {@Select("此处填写select的SQL语句并且表示填充的#{参数名A}")public List<Vo> queryVo(@Param("参数名A") String 参数B);
}

形参为对象

@Mapper
@DS("该类全部SQL方法调用的数据库")
//若整个项目仅有一个数据库则忽略该注解,若该注解加在方法上则是表示仅限该方法调用的数据库
public interface  VoMapper extends BaseMapper<Vo> {@Select("此处填写select的SQL语句并且表示填充的#{vo对象A.变量名}")public List<Vo> queryVo(@Param("vo对象A") Vo vo);
}

insert
SQL INSERT INTO 语法
INSERT INTO 语句可以有两种编写形式。

第一种全值插入形式无需指定要插入数据的列名,只需提供被插入的值即可:

INSERT INTO table_name
VALUES (value1,value2,value3,...);

第二种形式需要指定列名及被插入的值:

INSERT INTO table_name (column1,column2,column3,...)
VALUES (value1,value2,value3,...);

update
SQL UPDATE 语法

UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;

注解版SQL样例

script插入脚本声明
<script><foreach collection=\"updateUsers\" item=\"updateUser\"><if test=''></if></foreach>
</script>

注意

  • foreach

foreach中分割符separator的选定,若是多个sql拼接则需要用;作为分割符,若是多个字段拼接则需要用,作为分割符。
foreach标签的分隔符可以使用separator属性指定,默认为逗号。如果需要使用其他分隔符,可以在separator属性中指定。
标签属性,差一个空格都不行
item="updateUsers ":错误
item=“updateUsers”:正确

@Update("<script> " +"<foreach collection=\"updateUsers\" item=\"updateUser\" separator=\";\">" +" UPDATE" +" overview_audit_delay " +"<set>" +"<if test='updateUser.flage!=null'>" +"flage=#{updateUser.flage}, " +"</if>" +"<if test='updateUser.remarks!=null'>" +"remarks=#{updateUser.remarks}," +"</if>" +"update_time=#{updateUser.updateTime}" +"</set>" +" WHERE " +" user_id = #{updateUser.userId} and cycle_month = #{updateUser.cycleMonth} and deleted=0"+"</foreach>" +"</script>")@DS("objfzxserv")void updateRepAndRefUsers(@Param("updateUsers") List<AbnormalDelayUser> updateUsers);

条件判断更新

在更新时先判断传入的参数是否为null或空串以实现条件更新

 @Update({"<script> ","update tb_user_info set ","<if test = "uprofession != null"> ","uprofession=#{uprofession}, ","</if> ","<if test = "umajor != null"> ","umajor=#{umajor}, ","</if> ","<if test = "directionOfStudy != null"> ","directionOfStudy=#{directionOfStudy}, ","</if> ","update_time=CURRENT_TIMESTAMP ","WHERE user_id=#{user_id}","</script>"})void updateUserInfo(@Param("user_id") int user_id,@Param("uprofession") String uprofession,@Param("umajor") String umajor,@Param("directionOfStudy") String directionOfStudy);@Select({"<script>" ,"SELECT COUNT(*) FROM category","<if test='query != null and query != " " '>","where cat_name like '%${query}%'","</if>","</script>"})Integer getCount(@Param("query") String query);

批量操作

固定格式
<foreach>标签中的为循环遍历List数组拼接的SQL语句。

@crud({"<script>" +"<foreach collection=\"list数组名\" item=\"数组元素\" separator=\";\">" +"SQL语句和参数""</foreach>" +"</script>"})void method(@Param("list数组名")List数组);

list批量插入
@Insert批量插入
Mybatis积累(5):注解实现批量插入

@Repository
public interface UserMapper {public String tableName = "user";public String column = "username, password";@Insert("<script> " +"insert into " + tableName +"(" + column + ") " +"values " +"<foreach collection=\"items\" index=\"index\" item=\"item\" separator=\",\"> "+"(#{item.username},#{item.password})"+"</foreach> " +"</script>")int batchSave(@Param("items") List<User> items);

update批量更新
MyBatis Batch Update Exception使用foreach批量update出错

@Update({"<script>" +"<foreach collection=\"userInfoList\" item=\"item\" separator=\";\">" +" UPDATE" +" train_learn_task_user_info" +" SET completion_status = #{item.completionStatus, jdbcType=VARCHAR}" +" WHERE" +" id = #{item.id, jdbcType=BIGINT}" +"</foreach>" +"</script>"})void batchUpdate (@Param("userInfoList") List<LearnTaskUserInfo> userInfoList);

if-else
注意:case when then 只能用在select
固定开始:CASE
when (字段旧值判断) then 字段新值
else 字段新值
固定结束:END
as 别名

(case 
when (prov_code is NULL or prov_code='') then '00' 
else prov_code 
end) 
as provCode

service层

IService定义了简单单表的crud接口方法。

public interface VoService  extends IService<Vo> {public List<Vo> queryVo(String 参数);
}

serviceImpl层(代码逻辑实现层)

注意:MP封装的ServiceImpl通过baseMapper实现了service层IService中的基础crud方法框架,只需要自定义传参VoMapper extends BaseMapper<Vo>就可以实现功能了,可以ctr+左击进入查看一下。
Iservice定义的crud接口方法已经在ServiceImpl中用basemapper实现了框架,只缺VoMapper和Vo参数。

VoServiceImpl继承ServiceImpl实现的Service接口方法后就不需要再次实现对应接口方法。
只需要实现自定义的crud方法并且可以借助继承的baseMapper实现对应功能。
注意:ServiceImpl类上面一定要加注解这样才能用注解实例化对应service接口bean对象。

@Service
public class VoServiceImpl extends ServiceImpl<VoMapper, Vo> implements VoService {@ResourceVoMapper voMapper;@Overridepublic List<Vo> queryVo(String 参数) {//用自定义的VoMapper方法实现Service功能return voMapper.queryVo(参数);}
}

controller层(HTTP请求可以通过URL传参、请求头传参和请求体传参三种方式来传递参数。)

@RequestParam注解详细使用

  • @RequestMapping是一个无请求方法的注解。@GetMapping和@PostMapping是组合注解,分别是@RequestMapping(method = RequestMethod.GET)和@RequestMapping(method = RequestMethod.POST)的缩写。
  • 用POSTMapping和GetMapping代替RequestMapping,简化controller方法头注解。
  • @RequestMapping(value = “/Spath”, produces = “application/json;charset=UTF-8”, method = RequestMethod.GET)== @GetMapping(value = “/Spath”, produces = “application/json;charset=UTF-8”)
默认:@RequestParam==@RequestParam(value="参数名",required=true) String 参数名
特殊:@RequestParam(value="URL参数名",required=false) String 参数名

required=false:当URL中没有对应参数的时候也不报错而是给参数赋值为NULL。
注意:参数需为包装类
因为int,long等不能赋值为NULL。
若是required=true则基本数据类型就可以使用了。

@RequestParam和@RequestBody引入的get和post理解
POST、GET、@RequestBody和@RequestParam区别
@RequestParam可用于提取Get(URL)中请求参数。
@RequestBody只可用于提取Post的Body中的JSON请求参数封装到Vo实体类不可以用基本数据类型及其包装类作为参数。
(@RequestHeader Map<String, String> headers)用于提取http请求头的key-value键值对。

若是请求参数名和映射参数名一致则不需要特殊使用注解进行捆绑映射.

@RestController
@Slf4j
@RequestMapping("/Fpath")
public class VoController {@ResourceVoService voService;//此处注入的VoServiceImpl@RequestMapping(value = "/Spath", produces = "application/json;charset=UTF-8", method = RequestMethod.GET)public List<Vo> queryVo(@RequestParam(value="URL参数名",required=false) String 参数名){return voService.queryVo(参数);}
}
  • @RequestMapping(value = “/Spath”, produces = “application/json;charset=UTF-8”, method = RequestMethod.POST)== @PostMapping(value = “/Spath”, produces = “application/json;charset=UTF-8”)

HttpServletRequest详解
getParameter(String name) 根据name获取请求参数(常用)类Get方法
getParameterValues(String name) 根据name获取请求参数列表(常用)

@RestController
@Slf4j
@RequestMapping("/Fpath")
public class VoController {
@PostMapping("/Spath")
public ReturnMsg trigger(HttpServletRequest request, @RequestBody Vo vo) {Object x = vo.getDateTimeFmt();Object y = request.getParameter("S");//此处就是将URL(请求行)的S参数提取出来类似Get方法}
}

注意:SpringMVC @RequestParam
SpringMVC @RequestParam 推荐使用包装类型

required=false:当URL中没有对应参数的时候也不报错而是给参数赋值为NULL。
注意:参数需为包装类
因为int,long等不能赋值为NULL。
若是required=true则基本数据类型就可以使用了。

此处统一规定,参数全部使用基本数据类型的(首字母大写)包装类,保证不会出错。
String不属于基本数据类型为包装类。

Linux环境

程序执行操作系统命令、脚本、可执行文件并实现交互操作

  • Runtime.getRuntime().exec(command)用于执行操作系统命令、脚本、可执行文件等等
  • Process通过标准输入输出流(stdin/stdout)与之进行通信和交互
    Process类的常用方法包括:
    Runtime.getRuntime().exec(String command):启动一个外部进程并返回一个Process对象。
    process.getInputStream():获取子进程的标准输出流。
    process.getOutputStream():获取子进程的标准输入流。
    process.getErrorStream():获取子进程的标准错误流。
    process.waitFor():等待子进程结束并返回其退出值。
    通过这些方法,可以实现Java程序与其他外部程序的互动,例如读取外部程序的输出、向外部程序输入数据、等待外部程序执行完成等等。Process类在很多实际应用中都有广泛的使用,比如系统管理、文件处理、网络编程等等。

执行命令获取数据示例

Process process = Runtime.getRuntime().exec(command);try (InputStreamReader ip = new InputStreamReader(process.getInputStream(), GBK)) {try (BufferedReader br = new BufferedReader(ip)) {String fileContentLine;while ((fileContentLine = br.readLine()) != null) {//数据操作}}}

实战总结

乐观锁处理多线程“抢占”现象

两个SQL实现乐观锁

点睛之笔:单个SQL语句事务实现where判断和update占位

mp乐观锁实现方式:

  • First SQL:取出记录时,获取当前version(查)
    select version from table
  • Second SQL:更新时,带上这个version
  • 执行更新时
    update
    set version = newVersion (后占位)
    where version = oldVersion(先判断空位)
  • 如果version不对,就更新失败
    (先判后占)

乐观锁用于处理多线程’抢’现象
不只是乐观删除,乐观占用也行
乐观锁处理后则只有一个用户可以抢占成功,其余用户where判断空位失败

更新的三种方式

  • 一种是:传统update方式

前提:已知并确定待更新的单位。

  • 另一种是:先删后增的方式

未知待更新的单位并且可能会出现未存在单位需要insert则直接先删后增。

  • 还有一种是:查询,对比,增删改

查出来两份标识符,相同的更新,缺少的插入,多出来的删除。

Java杂七杂八

理解原理,归纳总结,生成步骤,方便照搬

    • 万物皆字符串(SQL,前后端交互JSON,文件流等等)
    • 一个网站是怎么来的?
  • IDEA操作
      • IDEA常用操作
      • Git可视化操作(提交代码前先pull更新merge最新版本一下再push,保证提交的最终项目是最新)
    • IDEA中Git冲突的产生及解决方法
      • Git冲突的原因通常有以下几种:
    • Idea如何查看本地自动保存的代码版本
  • 实战注意(BUG总结)
    • DEBUG方法
      • Mybatis_Plus的debug步骤
      • 快速测试本地方法
      • idea断点调试
    • 如何查找前端页面调用的后台接口
    • 开发完后测试前后端交互响应速度
    • 内存问题
    • 空指针问题
    • Git的pull项目后与本地IDEA的JDK不一致以及maven错启默认配置问题
    • Mybaits-Plus的SQL ERR报错
    • Every derived table must have its own alias(sql语句错误解决方法)
    • limit注意事项
    • 空格和传参问题(postman)
    • 优化慢SQL语句
    • 时空准则(程序优化准则)
    • 后端封装post请求时400状态码报错(后端转码与浏览器转码bug)
    • Mybatis的select结果集包含多个字段值的封装方式
    • [Java 移除List中的元素,这玩意讲究!]()
    • Mybatis注解版SQL注意事项
    • 万能数据结构:JSONArray=List\<Object>和JSONObject=Map<String,Object>
  • JAVA
    • Java中,String类型和包装类型作为参数传递时,是属于值传递还是引用传递呢?
    • Java语法
    • java默认访问权限
    • String的substring
    • 中文乱码问题
    • 单例模式
  • DateUtil工具类(Date,Calender,SimpleDateFormat)
    • 计算日期之间差值(年月日时分秒毫秒)
      • 前情提要
      • Java代码(基于SimpleDateFormat,计算任意符合日期格式的变量类型与当前时间差)
    • Date日期转化为Long类型
    • 获取当前Date日期的不同YMD格式String
    • Calender实现日期变化操作
      • Calender得到上月末后几天日期
      • Calender获得月份天数
    • Calender获取上月月份(YYYYMM)
  • StrUtil工具类(StringBuffer)
    • 字符串拼接方法
  • File文件操作
    • JAVA - 按行读取文件
    • JAVA - 将数据生成为TXT文件(PrintWriter)
      • JAVA流操作结束后为什么要关闭流
    • JAVA-文件删除
      • java file 跨盘符_File类——遍历盘符根目录查找文件报错 java.lang.NullPointerException.
  • SpringBoot
    • SpringBoot注解
      • 启动类位置注意事项(一定要注意文件结构)
      • @MapperScan
    • SpringBoot+MybatisPlus的MVC模板
      • 注解版SQL样例
    • SpringBoot定时任务
    • slf4j的简单用法以及与log4j的区别
      • try-catch配合log.error食用
  • Mybatis-Plus
    • Mybatis-Plus的自定义SQL操作(注解版)
      • 手动切换数据源(JdbcTemplate+DriverManagerDataSource)
      • mapper层固定[crud教程](.html)
        • ${}和#{}
        • 注解
        • 注解版SQL样例
        • 条件判断更新
        • 批量操作
    • service层
    • serviceImpl层(代码逻辑实现层)
    • controller层(HTTP请求可以通过URL传参、请求头传参和请求体传参三种方式来传递参数。)
  • Linux环境
    • 程序执行操作系统命令、脚本、可执行文件并实现交互操作
  • 实战总结
    • 乐观锁处理多线程“抢占”现象
    • 更新的三种方式

万物皆字符串(SQL,前后端交互JSON,文件流等等)

一个网站是怎么来的?

IDEA操作

IDEA常用操作

IDEA常用快捷键
CTR+ALT+L:快速格式化代码。
CTR+左击:类名/方法名则直接进入对应类和方法。

右上角放大镜搜索,可以设定搜索范围


IDEA查看并修改编解码方式

Git可视化操作(提交代码前先pull更新merge最新版本一下再push,保证提交的最终项目是最新)

注意:pull的次数不要太多,防止将别人测试版pull下来,push的时候和正式版产生冲突。
尽量初始化项目pull一次,提交之前pull一次,最后push。
若是产生冲突就需要新开窗口pull下来最新的cv上自己的然后push。
pull后融合,改动的地方以工作区为主,其余地方以远程仓库为主,若本地和远程相差过大则冲突
IDEA的git操作教程
pull下来远程仓库
方法一

此处若是未配置用户名和密码会弹出输入框

方法二
或者直接用上述办法一直接pull下来对应的远程仓库,这样会直接配置连接上对应的远程仓库。(若未输入用户名密码则会弹出来对应输入框)
配置连接远程数据库


选中项目然后右键

方法三


log:分支状态可视化
git log:查看当前(HEAD指向)分支的所有(empty到HEAD)版本


检查一遍

最后push


方法三

最终若是push出问题直接force push

IDEA中Git冲突的产生及解决方法

IDEA中Git冲突的产生及解决方法
冲突产生的根本原因是:多方改动后的同一个文件的同一块区域的内容不同(行列数定位)。
单一账号改动的同一区域内容不会冲突。
不同账号改动的同一区域同一行会冲突。
Git问题:1.push时候遇到错误,push失败
究其原因是为了保证head指向的代码同步为最新版本。

push失败的情况,是因为我们在push提交代码的时候,远程仓库已经发生变化了(远程head头指针与本地保存的远程head头指针指向不同),换句话说就是在这个期间(上一次拉取代码到本次提交代码),有其他人在我们之前提交了代码到我们想要推送的分支,导致远程仓库代码更新变化了。所以git拒绝了本次push。

Git冲突的原因通常有以下几种:

同时修改了同一行代码:当两个人同时修改了同一行代码时,Git无法判断哪个修改是正确的,因此会产生冲突。

修改了同一文件的不同部分:当两个人修改了同一文件的不同部分时,Git会尝试合并这些修改,但如果这些修改之间存在冲突(修改同一行),就会产生冲突。

合并分支时:当合并两个分支时,如果这两个分支都修改了同一文件的同一行,就会产生冲突。

当Git发现冲突时,会在冲突的文件中标记出冲突的部分,并在文件中添加特殊的标记,如"<<<<<<<
HEAD"和"=======“和”>>>>>>>",以表示冲突的部分。此时需要手动解决冲突,即选择哪个修改是正确的,然后将特殊标记删除,保存文件并提交修改。

Idea如何查看本地自动保存的代码版本


恢复历史数据

实战注意(BUG总结)

问题解决!关键是解决问题的思路,由易到难,有外到内,确保最基本的配置不出问题。
层级调用,注意不要在触发层存在Dao层代码。
定时任务整理数据入表,接口触发提取定时任务整理的数据表,缩短响应时间。

DEBUG方法

Mybatis_Plus的debug步骤

若是dao层SQL报错则从console复制传参SQL,到对应数据库跑跑试试看看具体问题
查看mp提供的拼接后的SQL,若排除SQL语法错误而是SQL不完整的原因报错则必因为service层传参值为空导致的SQL拼接报错。
例如:insert拼接不全SQL报错则是因为传参为空,导致values后面无值。

排除过程

  • 仔细检查 map文件 和数据库表字段没有错误;
  • 将生产的SQL,贴到Mysql Client端执行;
  • 再次检查JDBC驱动链接URL;

快速测试本地方法

@test和类中main方法

Java每个类可以有也可以没有main方法, 甚至所有类里可以都没有main方法。
如果你想从某个类做为入口开始运行整个程序。那么就把他设成 public ,之后再里面写个main方法作为入口。
每个项目都要有一个public主类,这个主类中必须得有main,用于程序的入口.在程序测试时,一般每个类中都有一个main,用于方便测试人员对类成员进行测试
不是,可有可无。但是你要执行的类中必须有,因为main函数是提供程序执行的进入口。比如你 java Test 那么Test类中必须有个main函数。也可拿来做测试某个类用。如你要测试一下Test类内的方法(这时的Test类可能不是主类,假设这时此类只提供给主类一些功能),那么你可以在Test类中加入一个main方法,调用Test类中的成员变量和方法,查看结果,达到测试的效果
————————————————
版权声明:本文为CSDN博主「小熊coder」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:

idea断点调试

如何查找前端页面调用的后台接口

通过F12进入浏览器开发者模式,然后进入network选项。

找到你想要知道接口的按钮,点击它,会出现如下图所示标红的区域,就是你访问接口的方法名。

箭头所指向的就是你想要访问的后台接口的地址。
也存在此方法的 请求方式 和 状态码 等等…

开发完后测试前后端交互响应速度

尽量减少数据库链接次数,链接数据库CRUD数据花费的时间很多(不要在for和while中出现DAO层操作)。
定时任务整理数据入表,接口触发提取定时任务整理的数据表,缩短响应时间。

内存问题

程序中所有定义的变量都会占内存,注意若是批量插入list等需要暂存大量数据的操作考虑一下会不会爆内存(达到数据量后需要先插入然后list.clear一下)。
注意:记得最后list数组内可能剩余未达到数量标准的数据,判一下list.size()>0然后循环外再插入。
java8解惑之字符串常量池(实现原理、垃圾回收)
结论:因为常量池是放在堆中,所以他的回收和普通对象是一样的,没有其他特别的地方。

空指针问题

String判空方式
用list数组接收SQL返回的数据后,调用前先判断一下list是否为空,防止SQL未查询到数据。
如何优雅的判空
CollectionUtils.isEmpty()作用:判断参数null或者其size0
关于CollectionUtils.isEmpty()
注意:“”内容为空但非空对象可用isEmpty判断,但是null只能用CollectionUtils.isEmpty

if (CollectionUtils.isEmpty(regionCountList)) {logger.info("{}日增量数据为空", date);}

例如:

实体类中包装类初始化为null然后和数值或其他相加等操作导致空指针报错

Git的pull项目后与本地IDEA的JDK不一致以及maven错启默认配置问题

JDK修改为一致1.8版本。


maven改为自定义环境(setting以及仓库)

Mybaits-Plus的SQL ERR报错

查看mp提供的拼接后的SQL,若排除SQL语法错误而是SQL不完整的原因报错则必因为service层传参值为空导致的SQL拼接报错。
例如:每次insert前都先判断一下是否为空。

Every derived table must have its own alias(sql语句错误解决方法)

Every derived table must have its own alias(sql语句错误解决方法)
当SQL中用另一个select的结果集作为from表时,必须给结果集起别名。

limit注意事项

SQL中limit的用法
limit子句用于限制查询结果返回的数量,常用于分页查询
格式

select * from tableName limit i,n
# tableName:表名
# i:为查询结果的索引值(默认从0开始),当i=0时可省略i
# n:为查询结果返回的数量
# i与n之间使用英文逗号","隔开
limit n 等同于 limit 0,n

例子

# 查询10条数据,索引从0到9,第1条记录到第10条记录
select * from t_user limit 10;
select * from t_user limit 0,10;# 查询8条数据,索引从5到12,第6条记录到第13条记录
select * from t_user limit 5,8;

空格和传参问题(postman)

空格
普通文本格式中的空格,复制到SQL文本窗口或者程序开发/测试文本窗口,可能会出问题。
所以若是持续出现格式报错则删除重写。
postman的body中粘贴json注意空格问题:复制的json需要逐一替换空格
传参
Json串的key:value中key值不能首字母大写,容易和Date,Long等java类冲突。

优化慢SQL语句

写完select语句在生产库执行一下,看一下执行时间,时间过长的话需要优化。
group by 可以节省select查询时间。
恶意order by导致数据库慢查询(除非有索引)

时空准则(程序优化准则)

空间换时间还是时间换空间
定位问题:需要空间则用时间换,需要时间则用空间换。
数据表索引(时空准则案例)

在SQL中,索引可以加快查询速度,因为它们允许数据库引擎更快地查找数据。索引是一种数据结构,它可以帮助数据库引擎快速定位表中的数据。当你在查询中使用索引列时,数据库引擎会使用索引来查找数据,而不是扫描整个表。这样可以大大减少查询的数据量,从而提高查询速度。你可以使用CREATE
INDEX语句来创建索引。例如,以下是一个使用CREATE INDEX语句创建索引的示例:

CREATE INDEX index_name ON table_name (column_name);

在这个示例中,我们使用CREATE
INDEX语句在table_name表的column_name列上创建了一个名为index_name的索引。

后端封装post请求时400状态码报错(后端转码与浏览器转码bug)

Server returned HTTP response code: 400 for URL

400是一种HTTP状态码,告诉客户端它发送了一条异常请求。400页面是当用户在打开网页时,返回给用户界面带有400提示符的页面。其含义是你访问的页面域名不存在或者请求错误。主要分为两种。
1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。
2、请求参数有误

  • 浏览器能访问但是后端封装post却不能的问题原因

这个问题算是后端转码与浏览器转码的一个坑吧 。
后端默认转码问题,手动将参数转码为utf-8就把汉字问题解决了。

  • url携带中文

将url中,中文部分进行转码

URLEncoder.encode(url, "utf-8");
  • url携带空格

URLEncoder 空格会被编码+,而URI中空格为%20

String encodeUrl = URLEncoder.encode(url, "utf-8");
encodeUrl = encodeUrl.replaceAll("\\+", "%20");

程序
访问前的URL:xxx?name=姓名==>结果访问失败

修改后的URL:xxx?name=URLEncoder.encode(“姓名”,“utf8”)==>访问成功
也可以直接将全部参数都utf-8编码,因为英文用任何编码都和ascall编码一样。
总结
如果程序访问一个url出现server returned HTTP Response code :400 fro URL这个错误,但是在浏览器中访问同样的url没问题的话,就要考虑是不是因为访问的url中有特殊字符。如空格、逗号、中文等。把这些特殊字符进行url编码后在使用程序进行访问或许就能成功了。需要注意,在进行url编码的时候,指定编码的utf-8字符集。

Mybatis的select结果集包含多个字段值的封装方式

方式一:实体类映射对应返回结果集多个字段并封装。
方式二:List<Map<String,Object>>
java.math.BigDecimal cannot be cast to java.lang.String
String封装字段名,Object封装字段值(默认为decimal类型):Object.toString()提取字段值。
注意:每个map封装的是一行记录,字段名为key,字段值为value。

Java 移除List中的元素,这玩意讲究!

注意:不可以在Java的foreach循环中remove元素。

Mybatis注解版SQL注意事项

mybatisCause: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 75; 元素内容必须由格式正确的字符数据或标记组成。

  • 复制SQL时;普通文本格式中的空格,复制到SQL文本窗口或者程序开发/测试文本窗口,可能会出问题。
  • 不能使用重载,因为SQL是根据方法名绑定,所以不能使用重载方法。
  • 添加脚本时:使用<>标签前一定要用<script>声明本SQL包含脚本。
  • 大小比较时:Mybatis注解版SQL可以使用<和>但是不能使用<=或者>=需要用转义字符替换,需要使用<script>声明才能用转义字符。
    xml格式的不允许出现类似“>”这样的字符也需要转义。
    org.xml.sax.SAXParseException: 元素内容必须由格式正确的字符数据或标记组成。 的解决办法
  • @DS:在Dao层绑定不了数据库则需要在IMPL层再@DS绑定一下数据库。

万能数据结构:JSONArray=List<Object>和JSONObject=Map<String,Object>

JSONArray 是一个有序的、元素不唯一的 JSON 数组,其数据结构类似于 Java 中的 List 或 Python 中的 List。

它由一对中括号 [] 包围,中间用逗号分隔每个元素。每个元素可以是 JSON 的任意数据类型,包括:

JSONObject:JSON 对象
JSONArray:JSON 数组
String:字符串
Number:数字(整数或浮点数)
Boolean:布尔值
null:空值

例如,以下是一个包含不同数据类型的 JSONArray:

["apple", 123, true, null, {"name": "John", "age": 30}, [1, 2, 3]]

JSONObject 可以非常灵活地表示各种数据结构
JSONObject 是一种用于存储和处理 JSON 数据的 Java 对象。它可以表示一个 JSON 对象,即由键值对组成的无序集合。在 JSONObject 中,键是字符串,值可以是基本数据类型(如整数、浮点数、布尔值、字符串)或者是其他的 JSONObject 或 JSONArray 对象。
举个例子,下面是一个 JSONObject 的示例:

{"name": "Alice","age": 25,"isStudent": true,"hobbies": ["reading", "swimming", "travelling"],"address": {"city": "Beijing","country": "China"}
}

JAVA

Java中,String类型和包装类型作为参数传递时,是属于值传递还是引用传递呢?

包装类型属于引用传递但是传参后方法改变数值后不会影响原数值。
因为包装类的数值为final类型所以不可改变而是会直接新建一个包装类对象来接收新数值(可比较内存地址查看是否为新建对象)。
其余自定义对象的引用(指针:内存地址)传值则可以在实例方法中改变对象值,原值同时改变


Java中的参数传递:分为值传递和引用传递
但本质上,Java中只有值传递。引用传递,其实可以理解为传的是类似指针的东西。
值传递就是把基本变量的值拷贝一份,传递这个拷贝。引用传递则是传递的引用的地址,也就是该变量在内存空间的地址
值传递
只有基本数据类型采用值传递,特点是传递的是值的拷贝,传递完后两者就没有关系了。也就是说方法内和方法外的值互不相干
基本数据类型:

  • 整型:int,long,byte,short
  • 浮点型:float,double
  • 字符型:char
  • 布尔型:boolean
    注:8种基本数据类型以外的数据类型都为引用类型。

引用传递
指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。
传递的是一个拷贝,即副本。也就是说,对于一个参数传递,存在两个地址指向同一个内存空间。这里我们可以用内存分配示意图来体现
String类型传递
先说结论,String类型传递与基本数据类型的传递效果相似。
说明:
String类对象一旦创建,其内容不可更改:
String类的所有方法都不会改变String类对象内容,要改变String类对象的值就必须创建一个新的String对象。
也就是说,当进行参数传递时,如果方法内对String类对象的值进行了修改,那么实际上是创建了一个新的String类对象,然后让原来的变量指向它而已。但是这个“原来的变量”是一份拷贝副本,只是一开始创建的时候与主方法中的传递的值相同而已,现在改变之后,两者就毫无关系了。

Java语法

java语法记录
java对象new实例之后,实例bean对应的变量也会赋初值(数值类型0或对象类型null)。

String初始值:null
Double初始值:null
Long初始值:null
double初始值:0.0
long初始值:0

java默认访问权限

Java默认访问权限

String的substring

注意:substring(start,end)中索引不能越界,否则报错。
java.lang.StringIndexOutOfBoundsException: String index out of range

中文乱码问题

对unicode的编码与解码方式不一致。
一听就能懂字符集、ASCII、GBK、Unicode、UTF-8、字符编码、解码、乱码问题的讲解

单例模式

工具类
构造方法private
全都是静态方法
类名.静态方法(参数)实现功能

DateUtil工具类(Date,Calender,SimpleDateFormat)

Java获取当前时间的上一年、下一年、上个月、下个月、前一天等(时间格式化)

计算日期之间差值(年月日时分秒毫秒)

注意:String可以作为任意类型转化为Date类型的中介
Object.toString()---->String–SimpleDateFormat.parse–>Date
Date–SimpleDateFormat.format–>String---->Object.parseObject(String)

前情提要

format时间格式
可带前导零的精确到毫秒的时间格式:yyyy-MM-dd HH:mm:ss.SSS:2022-09-28 16:19:08.338

注意:时间格式区分大小写
不要把月份的MM写为分钟mm
java中的的日期格式为:

yyyy-MM-dd HH:mm:ss:代表将时间转换为24小时制,例: 2020-01-07 13:21:55
yyyy-MM-dd hh:mm:ss: 代表将时间转换为12小时制,例: 2020-01-07 03:24:21

oracle中( to_char())的日期格式为(不区分大小写):
oracle日期格式转换 to_date(),to_char()
oracle 日期格式
(1)to_date(“要转换的字符串”,“转换的格式”) 两个参数的格式必须匹配,否则会报错。
是将字符串转化为日期(DATE)格式,而且转化之后的格式与orcal系统日期参数有关,
(2)to_char(日期,“转换格式” ) 即把给定的日期按照“转换格式”转换。
是将日期格式转化为字符串格式
Qracle中不区分大小写,MM和mm被认为是相同的格式代码,所以用mi替代mm

yyyy-MM-dd HH24:mi:ss:代表oracle中的24小时制,例:2020/1/7 13:21:55
yyyy-MM-dd HH:mi:ss: 代表oracle中的12小时制,例:2020/1/7 9:21:55

之所以 oracle和java不同,是因为我们知道oracle是不区分大小写的,所以java中根据大小写来代表24小时和12小时的表达式在oracle中就会出问题,oracle中将24小时的时和分做了特殊处理.如上所示,在hh后面加上了24,将mm改为了mi.
Java:

注意
Oracle数据库,今天根据时间日期查询展示数据,SQL语句是:
SELECT * FROM JCTJ_JXDB WHERE
DATAMONTH = TO_DATE(‘2021-11-29 15:12:54’,‘YYYY-MM-dd HH:mm:ss’),然后提示报错信息
SQL 错误 [1810] [22008]: ORA-01810: 格式代码出现两次
主要原因是由于Qracle中不区分大小写,MM和mm被认为是相同的格式代码,所以Oracle的SQL采用了mi代替分钟,如下修改为如下SQL即可:SELECT * FROM JCTJ_JXDB WHERE
DATAMONTH = TO_DATE(‘2021-11-29 15:12:54’,‘YYYY-MM-dd HH:mi:ss’),执行后,报如下错误提示:小时值必须介于1和12之间
这是由于我们使用的24小时制,如果我们的时间在0-12之间,是可以使用HH表示的,但现在我们查询的小时为15,大于12,所以在查询中需要使用HH24来表示小时

Java代码(基于SimpleDateFormat,计算任意符合日期格式的变量类型与当前时间差)

SimpleDateFormat
SimpleDateFormat格式化日期的方法和参数
SimpleDateFormat 报错:Unparseable date:String日期格式和format不匹配
String—SimpleDateFormat(format) —》Date
DateUtil工具类:

 		String lastTime="";String format="yyyy-MM-dd HH:mm:ss.SSS";//指定字符串的日期格式Long Day=DateUtil.currentDifferenceD(lastTime,format);if(Day==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");Long hour=DateUtil.currentDifferenceH(lastTime,format);if(hour==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");Long minute=DateUtil.currentDifferenceM(lastTime,format);if(minute==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");Long s=DateUtil.currentDifferenceS(lastTime,format);if(s==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");Long sss=DateUtil.currentDifferenceSSS(lastTime,format);if(sss==null) throw new IllegalArgumentException("Object转化Date求时间差失败,String日期格式和format不匹配");

工具类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class DateUtil {private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);private DateUtil(){}/*** 计算参数与当前时间的天数差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceD(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);//此方法仅可用于String类型转为Date类型,所以参数需要先转化为String类型Date date = formatter.parse(lastTime);Long nd=1000*60*60*24L;
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();diff=diff/nd;//计算差多少天return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}/*** 计算参数与当前时间的小时差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceH(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);Date date = formatter.parse(lastTime);Long nh=1000*60*60L;
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();diff=diff/nh;//计算差多少小时return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}/*** 计算参数与当前时间的分钟差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceM(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);Date date = formatter.parse(lastTime);Long nd=1000*60L;
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();diff=diff/nd;//计算差多少分钟return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}/*** 计算参数与当前时间的秒差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceS(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);Date date = formatter.parse(lastTime);Long nd=1000L;
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();diff=diff/nd;//计算差多少秒return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}/*** 计算参数与当前时间的毫秒差* lastTime:传入的符合format日期格式的对象* format:指定字符串的日期格式,然后根据日期格式解析String转为Date* return:若是传回null则Object转化Date失败*/public static Long currentDifferenceSSS(Object time,String format){String lastTime=time.toString();try {//当前日期Date dateNew = new Date(System.currentTimeMillis());//将数据库中String类型日期转化为Date类型SimpleDateFormat formatter= new SimpleDateFormat(format);Date date = formatter.parse(lastTime);
//        获得各个时间的毫秒时间差异 Date.getTime()返回自从GMT 1970-01-01 00:00:00到此Date对象上时间的毫秒数。Long diff=dateNew.getTime()-date.getTime();return diff;} catch (ParseException e) {logger.error(e.getMessage());}return null;}}

Date日期转化为Long类型

注意:String可以作为任意类型转化为Date类型的中介。
Object.toString()---->String–SimpleDateFormat.parse–>Date
Date–SimpleDateFormat.format–>String---->Object.parseObject(String)
因为日期可能为YYYYMMDD八位或者毫秒数更多所以只能用Long来存储,防止越界。

between…and…:可用于字符串以及日期

    /*** 将Date类型转化为Long类型*/public static Long dateToLong(Date date,String format){SimpleDateFormat formatter= new SimpleDateFormat(format);String time=formatter.format(date);return Long.parseLong(time);//String类型转为Long类型}

获得当前YYYYMM减去三个月后的YYYYMM

        SimpleDateFormat formatter= new SimpleDateFormat("yyyyMMdd");Calendar cal = Calendar.getInstance();cal.add(Calendar.MONTH, -3);Date result = cal.getTime();String lastMonth=formatter.format(result);System.out.println(lastMonth);
        //用毫秒数初始化构建Date对象Date dateNew = new Date(System.currentTimeMillis());SimpleDateFormat formatN = new SimpleDateFormat("yyyyMM");//获取1970-01-01 00:00:00到此Date对象上时间的毫秒数(初始化构建该对象的毫秒数)==System.currentTimeMillis().Long date = dateNew.getTime();//减去三个月的毫秒数date = date - 93L * 24L * 60L * 60L * 1000L;//操作完Long后再次初始化构建Date对象dateNew = new Date(date);String delMonth = formatN.format(dateNew);

获取当前Date日期的不同YMD格式String

    /*** 获取当前日期YYYYMMDD格式*/public static String getDateYYYYMMDD(){//当前日期Date dateNew = new Date(System.currentTimeMillis());SimpleDateFormat formatN=new SimpleDateFormat("yyyyMMdd");return formatN.format(dateNew);}/*** 获取当前账期YYYYMM格式*/public static String getDateYYYYMM(){//当前日期Date dateNew = new Date(System.currentTimeMillis());SimpleDateFormat formatN=new SimpleDateFormat("yyyyMM");return formatN.format(dateNew);}

Calender实现日期变化操作

Calender得到上月末后几天日期

例如:
正常情况下自己判断月末后五天是什么日期则需要考虑月份是30天还是31天如果是二月还要考虑闰年29天和平年28天情况。
Calender实现了这个功能。

        public static String getStartTime(int prevDay){Calendar c = Calendar.getInstance();Date date = c.getTime();//获取当前日期SimpleDateFormat formatN=new SimpleDateFormat("yyyyMMdd");String day =formatN.format(date).substring(6);//获取当天日期的天数int preDay = Integer.parseInt(day) + prevDay - 1;//获取当前天数与上个月倒数第prevDay天的差值c.add(Calendar.DATE,-(preDay));//当前日期减去插值return formatN.format(c.getTime());}

得到传入月份的上月末后prevDay天日期
传入:20221011,1
返回:20220930

    public static String getLastStartTime(String lastTime,int prevDay) {try{SimpleDateFormat formatN = new SimpleDateFormat("yyyyMMdd");Date date =formatN.parse(lastTime);Calendar c = Calendar.getInstance();c.setTime(date);String day = formatN.format(date).substring(6);int preDay = Integer.parseInt(day) + prevDay - 1;c.add(Calendar.DATE, -(preDay));return formatN.format(c.getTime());}catch (ParseException e) {logger.error(e.getMessage());}return null;}

Calender获得月份天数

   /*** 传入时间 获得天数 如果要得到其他月份的天数 设置对象的月份以及年份即可* */public static int getMonthDay(String yyyymm) {Calendar a = Calendar.getInstance();//获取当前时间Integer year= Integer.valueOf(yyyymm.substring(0,4));Integer month=Integer.valueOf(yyyymm.substring(4));a.set(Calendar.YEAR, year);a.set(Calendar.MONTH, month - 1);// Calendar月份是以0开始的 所以要-1a.set(Calendar.DATE, 1);//把日期设置为当月第一天a.roll(Calendar.DATE, -1);//日期回滚一天,也就是最后一天int day = a.get(Calendar.DATE);return day;}

Calender获取上月月份(YYYYMM)

    /*** 获取上月月份*/public static String getLastMonth(){SimpleDateFormat format = new SimpleDateFormat("yyyyMM");Date date = new Date();Calendar calendar = Calendar.getInstance();// 设置为当前时间calendar.setTime(date);calendar.add(Calendar.MONTH,-1);// 设置为上一个月date = calendar.getTime();return format.format(date);}

StrUtil工具类(StringBuffer)

String类型传递
先说结论,String类型传递与基本数据类型的传递效果相似。
说明:
String类对象一旦创建,其内容不可更改:
String类的所有方法都不会改变String类对象内容,要改变String类对象的值就必须创建一个新的String对象。
也就是说,当进行参数传递时,如果方法内对String类对象的值进行了修改,那么实际上是创建了一个新的String类对象,然后让原来的变量指向它而已。但是这个“原来的变量”是一份拷贝副本,只是一开始创建的时候与主方法中的传递的值相同而已,现在改变之后,两者就毫无关系了。

字符串拼接方法

Java中方法的参数传递机制以及形参个数可变的方法

    /*以可变个数形参来定义方法,可传入任意个数String字符串参数String... strings*/public  static String strSplice(String... strings){StringBuffer stringBuffer=new StringBuffer();for(String s:strings){stringBuffer.append(s);}return stringBuffer.toString();}

File文件操作

JAVA - 按行读取文件

  /*** 文件按行解析*/public static Map<Integer, String> parseFile(String path, String fileName) {File file = new File(path + "/" + fileName);Map<Integer, String> lineData = new HashMap<>(16);try (BufferedReader reader = new BufferedReader(new FileReader(file));) {String tempString = null;int line = 0;while ((tempString = reader.readLine()) != null) {lineData.put(++line, tempString);}} catch (Exception e) {logger.error(e.getMessage());}return lineData;}

JAVA - 将数据生成为TXT文件(PrintWriter)

JAVA - 将数据生成为TXT文件(可追加append,可重写)

JSONArray用法
测试用例
注意:java中表示文件夹目录是类似如下格式(结尾是\\否则认定最后一层为文件名)
windows路径格式
String path=“D:\\Atemp\\…\\”;
linux路径格式
String path=“/home/xxx/data/…/”;

JSONArray jsonArray=new JSONArray();JSONObject obj = new JSONObject();JSONObject obj1 = new JSONObject();obj.put("orderId",456);obj.put("packageId", 789);obj.put("startTime", 789);obj1.put("orderId",456);obj1.put("packageId", 789);obj1.put("startTime", 789);jsonArray.add(obj);jsonArray.add(obj1);// 这是用于访问操作系统临时目录的属性名称// 或文件夹。String property = "java.io.tmpdir";// 获取临时目录并打印。String tempDir = System.getProperty(property);System.out.println("OS temporary directory is " + tempDir);createTxtFile(jsonArray,tempDir,"AAA");
  public static boolean createTxtFile(JSONArray jsonArray, String path, String filename) {// 标记文件生成是否成功boolean flag = true;try {// 含文件名的全路径String[] strings = {path, filename, ".txt"};String fullPath = StrUtils.strSplice(strings);File file = new File(fullPath);File folder = new File(path);if (!folder.exists() && !folder.isDirectory()) {// 如果不存在,创建文件夹folder.mkdirs();}if (!file.exists()) {file.createNewFile();}// 格式化浮点数据NumberFormat formatter = NumberFormat.getNumberInstance();// 设置最大小数位为10formatter.setMaximumFractionDigits(10);// 格式化日期数据SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");// 遍历输出每行PrintWriter pfp = new PrintWriter(new FileOutputStream(file, true));for (int i = 0; i < jsonArray.size(); i++) {JSONObject jsonObject = jsonArray.getJSONObject(i);if (jsonObject.isEmpty()) {break;}StringBuilder thisLine = new StringBuilder("");for (Iterator<String> iterator = jsonObject.keySet().iterator(); iterator.hasNext(); ) {// 当前字段String key = iterator.next();Object obj = jsonObject.get(key);// 格式化数据String field = "";if (null != obj) {if (obj.getClass() == String.class) {// 如果是字符串field = (String) obj;} else if (obj.getClass() == Double.class || obj.getClass() == Float.class) {// 格式化浮点数,使浮点数不以科学计数法输出field = formatter.format(obj);} else if (obj.getClass() == Integer.class || obj.getClass() == Long.class|| obj.getClass() == Short.class || obj.getClass() == Byte.class) { // 如果是整形field += obj;} else if (obj.getClass() == Date.class) {// 如果是日期类型field = sdf.format(obj);}} else {// null时给一个空格占位field = " ";}// 拼接所有字段为一行数据,用逗号分隔// 不是最后一个元素if (iterator.hasNext()) {thisLine.append(field).append(",");} else {// 是最后一个元素thisLine.append(field);}}pfp.print(thisLine.toString() + "\n");}pfp.close();} catch (Exception e) {flag = false;log.error("生成txt文件失败", e);}return flag;}

JAVA流操作结束后为什么要关闭流

需要自己close的东西,一般都是用了虚拟机之外的资源,例如端口,显存,文件等,虚拟机无法通过垃圾回收释放这些资源,只能你显式调用close方法来释放。

许多情况下,如果在一些比较频繁的操作中,不对流进行关闭,很容易出现输入输出流经超越了JVM的边界,所以有时可能无法回收资源。
所以流操作的时候凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。

你读写一个文件,忘记关闭了流,你在操作系统里对这个文件的写,删除等操作就会报错,告诉你这个文件被某个进程占用

JAVA-文件删除

此方法是遍历给定file目录下的全部文件夹以及文件查找对应fileName文件来删除。

public class Demo {public static void main(String[] args) {//创建File对象,给定磁盘目录File file = new File("D:\\work");//设定待删除文件的名称String fileName = "xxx.txt";//把file对象传递给方法getFiledeleteFile(file,fileName);}public static void deleteFile(File file, String fileName) {if (file.exists()) {//获取file类的所有文件和目录File[] files = file.listFiles();if (files != null) {for (File f : files) {//判断是否为目录,如果为目录则利用递归继续进行寻找文件if (f.isDirectory()) {deleteFile(f, fileName);}//不是目录,那就是文件,则判断文件后缀是否为.txt,如果是则删除if (f.getName().equals(fileName)) {System.out.println(f.getName());f.delete();//log.infor(fileName + "文件删除完毕!")System.out.println(fileName + "文件删除完毕!");}}}}}

java file 跨盘符_File类——遍历盘符根目录查找文件报错 java.lang.NullPointerException.

注意:一定要判断file.exists并且files!=null才可以继续执行。
因为遍历到System Volume Information目录时,用户没有访问权限,所以调用file.listFile方法会返回null给files数组而不是file目录下的全部文件,所以循环体中数组元素调用方法的时候会产生空指针异常。

System Volume Information”是windows系统文件夹

是隐藏文件

中文名称可以翻译为“系统卷标信息”

这个文件夹里就存储着系统还原的备份信息

System Volume Information 文件夹是一个隐藏的系统文件夹

"系统还原"工具使用该文件夹来存储它的信息和还原点

例如:

SpringBoot

SpringBoot注解

Spring全家桶回顾

启动类位置注意事项(一定要注意文件结构)

SpringBoot启动类配置@Scan扫描包注解后,对应basePackages = "XXX.xxx"包下的类或接口等就不需要再添加对应@注解了。
 

AppApplication 一定要在包的最外层,否则Spring无法对所有的类进行托管,会造成@Autowired 无法注入。
这是因为SpringBoot项目的注解Bean装配默认规则是根据AppApplication 类所在的包位置从上往下扫描!即只会扫描AppApplication 所在的包及其子包,其他包路径不会被扫描!!!


若想扫描其他包中配置类则需要配置@ComponentScan。

@MapperScan

作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类。
配置扫描的是java接口不是xml文件,而maven默认不扫描xml文件,需要单独配置maven扫描xml文件。
@MapperScan注解

———————————————— 版权声明:本文为CSDN博主「哈6哈6」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:

SpringBoot+MybatisPlus的MVC模板

Mybatis积累
注解版Mapper省去了绑定xml和Mapper类的步骤,直接将SQL以及数据库和Mapper方法绑定在一起。

注解版SQL样例

script插入脚本声明
<script><foreach collection=\"updateUsers\" item=\"updateUser\" separator=\";或,\"><if test=''></if></foreach>
</script>

注意:foreach中分割符separator的选定,若是多个sql拼接则需要用;作为分割符,若是多个字段拼接则需要用,作为分割符。
foreach标签的分隔符可以使用separator属性指定,默认为逗号。如果需要使用其他分隔符,可以在separator属性中指定。

@Update("<script> " +"<foreach collection=\"updateUsers\" item=\"updateUser\" separator=\";\">" +" UPDATE" +" overview_audit_delay " +"<set>" +"<if test='updateUser.flage!=null'>" +"flage=#{updateUser.flage}, " +"</if>" +"<if test='updateUser.remarks!=null'>" +"remarks=#{updateUser.remarks}," +"</if>" +"update_time=#{updateUser.updateTime}" +"</set>" +" WHERE " +" user_id = #{updateUser.userId} and cycle_month = #{updateUser.cycleMonth} and deleted=0"+"</foreach>" +"</script>")@DS("objfzxserv")void updateRepAndRefUsers(@Param("updateUsers") List<AbnormalDelayUser> updateUsers);

SpringBoot定时任务

定时任务就是定时触发service方法。
SpringBoot定时任务
启动类添加注解:@EnableScheduling,定时类添加注解:@Component,定时方法添加注解@Scheduled(cron=“cron表达式”或fixed注解)
@EnableScheduling //可以在启动类上注解也可以在执行调度任务类

@Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行
@Scheduled(fixedDelay= 5000) :上一次执行完毕时间点之后5秒再执行
@Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
@Scheduled(cron="0*/1 * * * ? ") :通过cron表达式定义规则,表示每隔1分钟执行一次

slf4j的简单用法以及与log4j的区别

log的最大好处是可以在docker容器的终端环境中查看对应记录。
slf4j的简单用法以及与log4j的区别

package cn.xm.exam.test;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jTest {private static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);// slf4j日志记录器public static void main(String[] args) {// 普通的日志记录logger.debug("普通的日志记录");// {}占位符记录日志for (int i = 0; i < 3; i++) {logger.debug("这是第{}条记录", i);}// 用\转义{}logger.debug("Set \\{} differs from {}", "3"); // output:Set {} differs// from 3// 两个参数logger.debug("两个占位符,可以传两个参数{}----{}", 1, 2);// 多个参数(可变参数)logger.debug("debug:多个占位符,{},{},{},{}", 1, 2, 3, 4);// 多个参数(可变参数)logger.info("info:多个占位符,{},{},{},{}", 1, 2, 3, 4);// 多个参数(可变参数)logger.error("error:多个占位符,{},{},{},{}", 1, 2, 3, 4);}}

@Slf4j是用作日志输出的,一般会在项目每个类的开头加入该注解,如果不写下面这段代码,并且想用log

private final Logger logger = LoggerFactory.getLogger(当前类名.class);

就可以用@Slf4来代替;这样就省去这段很长的代码。
默认log变量实现对应功能。


@Controller
@RequestMapping("/abc")
@Slf4j
public class QueryBillController {@Autowiredprivate TraceService traceService;@Autowiredprivate SignatureAndVerification signatureAndVerification;/*** 账单查询接口*/@RequestMapping(.........)@ResponseBodypublic String getBills(@RequestBody String queryRequest) {log.info("进入账单查询接口");

添加了该注释之后,就可以在代码中直接使用log.info( ) 等打印日志了

try-catch配合log.error食用

  try{//关闭数据库资源连接Connection conn = dataSource.getConnection();conn.close();}catch (Exception e){logger.error("数据库资源释放失败"+e.getMessage());}

Mybatis-Plus

写好SQL后先去数据库跑跑看看是否正确。

Mybatis-Plus的自定义SQL操作(注解版)

下图对应的Mapper层和service层以及serviceImpl层的类声明以及注解(@Mapper,@Service)都是MP规范格式模板。
实现了MP制定好的简单单表CRUD方法声明(Service)和方法实现(ServiceImpl基于VoMapper),只需要传参VoMapper和Vo即可.(若多个数据库则需要@DS指定进行自定义SQL操作,不可用封装方法)
interface接口都是声明crud方法,class才是基于实例化base/VoMapper等实现crud方法
继承复用了MP制定的规范格式模板,可以直接Mapper调用MP封装的CRUD方法实现功能

model层(实体类命名与数据字段名要协调)
原理:Vo实体类变量封装映射的是select的结果集,可以通过as给结果集起别名来匹配Vo的变量名。
mybatis的sql中字段两种映射(映射到实体)方式

@TableField(select = false)    //查询时,则不返回该字段的值
@TableField(value = "字段名")    //通过tableField进行字段名和变量名不一致的映射
@TableField(exist = false)  //声明该字段在数据库表中不存在,不参与数据库表的映射
@Data
public class Vo{
private Long 对应数据表 bigInt
private String 对应数据表 varchar
}

手动切换数据源(JdbcTemplate+DriverManagerDataSource)

该方法适应于需要根据传参动态切换数据源的程序。
若是仅仅想用JdbcTemplate提升效率则可以配合@DS食用。

JdbcTemplate是Spring Framework中一个核心的JDBC模板类,它简化了JDBC的开发流程,封装了JDBC的重复代码,使得开发者能够更加方便快捷地进行数据库访问。

使用JdbcTemplate,需要创建一个JdbcTemplate对象,可以通过构造函数注入数据源对象(如:DriverManagerDataSource、C3P0DataSource、BoneCPDataSource等)或者通过setDataSource()方法注入数据源对象。之后,就可以通过该JdbcTemplate对象执行SQL语句,比如:查询数据、插入数据、更新数据、删除数据等。

//手动设置数据源DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/test");dataSource.setUsername("root");dataSource.setPassword("root");
// 通过构造函数传入数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 或者通过setDataSource方法设置数据源
jdbcTemplate.setDataSource(dataSource);jdbcTemplate.增删改查//用完之后需要关闭DriverManagerDataSource连接try{//关闭数据库资源连接Connection conn = dataSource.getConnection();conn.close();}catch (Exception e){logger.error("数据库资源释放失败"+e.getMessage());}
 - 查询单个值
String sql = "SELECT COUNT(*) FROM users";
int count = jdbcTemplate.queryForObject(sql, Integer.class);- 查询单个对象
String sql = "SELECT * FROM users WHERE id=?";
User user = jdbcTemplate.queryForObject(sql, new Object[] { id }, new BeanPropertyRowMapper<>(User.class));- 查询多个对象
String sql = "SELECT * FROM users";
List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));- 带参数查询多个对象
String sql = "SELECT * FROM users WHERE age > ?";
List<User> users = jdbcTemplate.query(sql, new Object[] { age }, new BeanPropertyRowMapper<>(User.class));- 查询多个对象,并分页
String sql = "SELECT * FROM users";
int pageNumber = 1;
int pageSize = 10;
int startIndex = (pageNumber - 1) * pageSize;
List<User> users = jdbcTemplate.query(sql + " LIMIT ?, ?", new Object[] { startIndex, pageSize }, new BeanPropertyRowMapper<>(User.class));- 查询多个对象,并按字段排序
String sql = "SELECT * FROM users ORDER BY age DESC";
List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));- 执行查询并处理结果集
String sql = "SELECT * FROM users";
jdbcTemplate.query(sql, rs -> {while (rs.next()) {int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");// 处理结果集}
});- 执行查询,并自定义结果集处理器
String sql = "SELECT * FROM users";
jdbcTemplate.query(sql, new MyRowMapper());
class MyRowMapper implements RowMapper<User> {@Overridepublic User mapRow(ResultSet rs, int rowNum) throws SQLException {int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");// 处理结果集return new User(id, name, age);}
}- 执行查询返回List<Map<String, Object>>
public List<Map<String, Object>> queryData() {return jdbcTemplate.queryForList("SELECT * FROM user");
}- 执行查询返回Map<String, Object>
public Map<String, Object> queryData() {return jdbcTemplate.queryForList("SELECT * FROM user limit 1");
}

mapper层固定crud教程

自定义Vomapper继承baseMapper的作用主要是基于baseMapper简单单表crud的基础上,添加自定义crud方法。

${}和#{}

mybatis中的#和$的区别
#{} 和 ${} 的区别 — 详细!
大多数情况下还是经常使用#,一般能用#的就别用$;但有些情况下必须使用$,传入表名或字段名的时候,例:MyBatis排序时使用order by 动态参数时需要注意,用$而不是#
#传入的参数在SQL中显示为字符串,$传入的参数在SQL中直接显示为传入的值.

#不需要加’‘,本身自带’',$需要按照情况分析。

效果上: #{}=‘${}’=‘参数’

SQL中字符串可以直接比较,并且其它类型也可以直接转为字符串比较。

where id = 66和where id = '66'等效

注解

@Param
首先明确这个注解是为SQL语句中参数赋值而服务的。

  @Param的作用就是给参数命名,比如在mapper里面某方法A(int id),当添加注解后A(@Param("userId") int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传如SQL语句中,通过#{userId}进行取值给SQL的参数赋值。


或者spring.datasource.custom.name 配置默认数据源

@Mapper
@DS("该类全部SQL方法调用的数据库")
//若整个项目仅有一个数据库则忽略该注解,若该注解加在方法上则是表示仅限该方法调用的数据库
public interface  VoMapper extends BaseMapper<Vo> {@Select("此处填写select的SQL语句并且表示填充的#{参数名A}")public List<Vo> queryVo(@Param("参数名A") String 参数B);
}

形参为对象

@Mapper
@DS("该类全部SQL方法调用的数据库")
//若整个项目仅有一个数据库则忽略该注解,若该注解加在方法上则是表示仅限该方法调用的数据库
public interface  VoMapper extends BaseMapper<Vo> {@Select("此处填写select的SQL语句并且表示填充的#{vo对象A.变量名}")public List<Vo> queryVo(@Param("vo对象A") Vo vo);
}

insert
SQL INSERT INTO 语法
INSERT INTO 语句可以有两种编写形式。

第一种全值插入形式无需指定要插入数据的列名,只需提供被插入的值即可:

INSERT INTO table_name
VALUES (value1,value2,value3,...);

第二种形式需要指定列名及被插入的值:

INSERT INTO table_name (column1,column2,column3,...)
VALUES (value1,value2,value3,...);

update
SQL UPDATE 语法

UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;

注解版SQL样例

script插入脚本声明
<script><foreach collection=\"updateUsers\" item=\"updateUser\"><if test=''></if></foreach>
</script>

注意

  • foreach

foreach中分割符separator的选定,若是多个sql拼接则需要用;作为分割符,若是多个字段拼接则需要用,作为分割符。
foreach标签的分隔符可以使用separator属性指定,默认为逗号。如果需要使用其他分隔符,可以在separator属性中指定。
标签属性,差一个空格都不行
item="updateUsers ":错误
item=“updateUsers”:正确

@Update("<script> " +"<foreach collection=\"updateUsers\" item=\"updateUser\" separator=\";\">" +" UPDATE" +" overview_audit_delay " +"<set>" +"<if test='updateUser.flage!=null'>" +"flage=#{updateUser.flage}, " +"</if>" +"<if test='updateUser.remarks!=null'>" +"remarks=#{updateUser.remarks}," +"</if>" +"update_time=#{updateUser.updateTime}" +"</set>" +" WHERE " +" user_id = #{updateUser.userId} and cycle_month = #{updateUser.cycleMonth} and deleted=0"+"</foreach>" +"</script>")@DS("objfzxserv")void updateRepAndRefUsers(@Param("updateUsers") List<AbnormalDelayUser> updateUsers);

条件判断更新

在更新时先判断传入的参数是否为null或空串以实现条件更新

 @Update({"<script> ","update tb_user_info set ","<if test = "uprofession != null"> ","uprofession=#{uprofession}, ","</if> ","<if test = "umajor != null"> ","umajor=#{umajor}, ","</if> ","<if test = "directionOfStudy != null"> ","directionOfStudy=#{directionOfStudy}, ","</if> ","update_time=CURRENT_TIMESTAMP ","WHERE user_id=#{user_id}","</script>"})void updateUserInfo(@Param("user_id") int user_id,@Param("uprofession") String uprofession,@Param("umajor") String umajor,@Param("directionOfStudy") String directionOfStudy);@Select({"<script>" ,"SELECT COUNT(*) FROM category","<if test='query != null and query != " " '>","where cat_name like '%${query}%'","</if>","</script>"})Integer getCount(@Param("query") String query);

批量操作

固定格式
<foreach>标签中的为循环遍历List数组拼接的SQL语句。

@crud({"<script>" +"<foreach collection=\"list数组名\" item=\"数组元素\" separator=\";\">" +"SQL语句和参数""</foreach>" +"</script>"})void method(@Param("list数组名")List数组);

list批量插入
@Insert批量插入
Mybatis积累(5):注解实现批量插入

@Repository
public interface UserMapper {public String tableName = "user";public String column = "username, password";@Insert("<script> " +"insert into " + tableName +"(" + column + ") " +"values " +"<foreach collection=\"items\" index=\"index\" item=\"item\" separator=\",\"> "+"(#{item.username},#{item.password})"+"</foreach> " +"</script>")int batchSave(@Param("items") List<User> items);

update批量更新
MyBatis Batch Update Exception使用foreach批量update出错

@Update({"<script>" +"<foreach collection=\"userInfoList\" item=\"item\" separator=\";\">" +" UPDATE" +" train_learn_task_user_info" +" SET completion_status = #{item.completionStatus, jdbcType=VARCHAR}" +" WHERE" +" id = #{item.id, jdbcType=BIGINT}" +"</foreach>" +"</script>"})void batchUpdate (@Param("userInfoList") List<LearnTaskUserInfo> userInfoList);

if-else
注意:case when then 只能用在select
固定开始:CASE
when (字段旧值判断) then 字段新值
else 字段新值
固定结束:END
as 别名

(case 
when (prov_code is NULL or prov_code='') then '00' 
else prov_code 
end) 
as provCode

service层

IService定义了简单单表的crud接口方法。

public interface VoService  extends IService<Vo> {public List<Vo> queryVo(String 参数);
}

serviceImpl层(代码逻辑实现层)

注意:MP封装的ServiceImpl通过baseMapper实现了service层IService中的基础crud方法框架,只需要自定义传参VoMapper extends BaseMapper<Vo>就可以实现功能了,可以ctr+左击进入查看一下。
Iservice定义的crud接口方法已经在ServiceImpl中用basemapper实现了框架,只缺VoMapper和Vo参数。

VoServiceImpl继承ServiceImpl实现的Service接口方法后就不需要再次实现对应接口方法。
只需要实现自定义的crud方法并且可以借助继承的baseMapper实现对应功能。
注意:ServiceImpl类上面一定要加注解这样才能用注解实例化对应service接口bean对象。

@Service
public class VoServiceImpl extends ServiceImpl<VoMapper, Vo> implements VoService {@ResourceVoMapper voMapper;@Overridepublic List<Vo> queryVo(String 参数) {//用自定义的VoMapper方法实现Service功能return voMapper.queryVo(参数);}
}

controller层(HTTP请求可以通过URL传参、请求头传参和请求体传参三种方式来传递参数。)

@RequestParam注解详细使用

  • @RequestMapping是一个无请求方法的注解。@GetMapping和@PostMapping是组合注解,分别是@RequestMapping(method = RequestMethod.GET)和@RequestMapping(method = RequestMethod.POST)的缩写。
  • 用POSTMapping和GetMapping代替RequestMapping,简化controller方法头注解。
  • @RequestMapping(value = “/Spath”, produces = “application/json;charset=UTF-8”, method = RequestMethod.GET)== @GetMapping(value = “/Spath”, produces = “application/json;charset=UTF-8”)
默认:@RequestParam==@RequestParam(value="参数名",required=true) String 参数名
特殊:@RequestParam(value="URL参数名",required=false) String 参数名

required=false:当URL中没有对应参数的时候也不报错而是给参数赋值为NULL。
注意:参数需为包装类
因为int,long等不能赋值为NULL。
若是required=true则基本数据类型就可以使用了。

@RequestParam和@RequestBody引入的get和post理解
POST、GET、@RequestBody和@RequestParam区别
@RequestParam可用于提取Get(URL)中请求参数。
@RequestBody只可用于提取Post的Body中的JSON请求参数封装到Vo实体类不可以用基本数据类型及其包装类作为参数。
(@RequestHeader Map<String, String> headers)用于提取http请求头的key-value键值对。

若是请求参数名和映射参数名一致则不需要特殊使用注解进行捆绑映射.

@RestController
@Slf4j
@RequestMapping("/Fpath")
public class VoController {@ResourceVoService voService;//此处注入的VoServiceImpl@RequestMapping(value = "/Spath", produces = "application/json;charset=UTF-8", method = RequestMethod.GET)public List<Vo> queryVo(@RequestParam(value="URL参数名",required=false) String 参数名){return voService.queryVo(参数);}
}
  • @RequestMapping(value = “/Spath”, produces = “application/json;charset=UTF-8”, method = RequestMethod.POST)== @PostMapping(value = “/Spath”, produces = “application/json;charset=UTF-8”)

HttpServletRequest详解
getParameter(String name) 根据name获取请求参数(常用)类Get方法
getParameterValues(String name) 根据name获取请求参数列表(常用)

@RestController
@Slf4j
@RequestMapping("/Fpath")
public class VoController {
@PostMapping("/Spath")
public ReturnMsg trigger(HttpServletRequest request, @RequestBody Vo vo) {Object x = vo.getDateTimeFmt();Object y = request.getParameter("S");//此处就是将URL(请求行)的S参数提取出来类似Get方法}
}

注意:SpringMVC @RequestParam
SpringMVC @RequestParam 推荐使用包装类型

required=false:当URL中没有对应参数的时候也不报错而是给参数赋值为NULL。
注意:参数需为包装类
因为int,long等不能赋值为NULL。
若是required=true则基本数据类型就可以使用了。

此处统一规定,参数全部使用基本数据类型的(首字母大写)包装类,保证不会出错。
String不属于基本数据类型为包装类。

Linux环境

程序执行操作系统命令、脚本、可执行文件并实现交互操作

  • Runtime.getRuntime().exec(command)用于执行操作系统命令、脚本、可执行文件等等
  • Process通过标准输入输出流(stdin/stdout)与之进行通信和交互
    Process类的常用方法包括:
    Runtime.getRuntime().exec(String command):启动一个外部进程并返回一个Process对象。
    process.getInputStream():获取子进程的标准输出流。
    process.getOutputStream():获取子进程的标准输入流。
    process.getErrorStream():获取子进程的标准错误流。
    process.waitFor():等待子进程结束并返回其退出值。
    通过这些方法,可以实现Java程序与其他外部程序的互动,例如读取外部程序的输出、向外部程序输入数据、等待外部程序执行完成等等。Process类在很多实际应用中都有广泛的使用,比如系统管理、文件处理、网络编程等等。

执行命令获取数据示例

Process process = Runtime.getRuntime().exec(command);try (InputStreamReader ip = new InputStreamReader(process.getInputStream(), GBK)) {try (BufferedReader br = new BufferedReader(ip)) {String fileContentLine;while ((fileContentLine = br.readLine()) != null) {//数据操作}}}

实战总结

乐观锁处理多线程“抢占”现象

两个SQL实现乐观锁

点睛之笔:单个SQL语句事务实现where判断和update占位

mp乐观锁实现方式:

  • First SQL:取出记录时,获取当前version(查)
    select version from table
  • Second SQL:更新时,带上这个version
  • 执行更新时
    update
    set version = newVersion (后占位)
    where version = oldVersion(先判断空位)
  • 如果version不对,就更新失败
    (先判后占)

乐观锁用于处理多线程’抢’现象
不只是乐观删除,乐观占用也行
乐观锁处理后则只有一个用户可以抢占成功,其余用户where判断空位失败

更新的三种方式

  • 一种是:传统update方式

前提:已知并确定待更新的单位。

  • 另一种是:先删后增的方式

未知待更新的单位并且可能会出现未存在单位需要insert则直接先删后增。

  • 还有一种是:查询,对比,增删改

查出来两份标识符,相同的更新,缺少的插入,多出来的删除。

与本文相关的文章

发布评论

评论列表 (0)

  1. 暂无评论