回顾自己程序员生涯中踩过的那些坑:
1,一开始的时候抱着什么都不会有人肯招就去的心态去了一家很小的传统软件公司,是写高校后台系统的,就是学生审批,成绩之类的学工系统。当时遇到一个需求是计算学生的绩点,你能想象我是蠢到一个学生学号去数据库查数据然后for循环N次吗?,当然直接慢到超时了,然后项目经理一个30多岁的大姐姐看了一眼后告诉了我人生中第一条写代码规则:别在for循环里一遍一遍查数据库!一开始我并不理解,只是当成准则记在心里,觉得可能是这样效率会很差?为什么会差呢?不知道。直到后来面试的时候别人问我你写代码的时候遇到过什么难关吗?我就说了这个,然后面试官当时就急了,怎么会只是效率差?这样数据库连接就....。当然不用想这家我肯定面试不过,太差了,我自己现在想想都太差了。
2,还是那家传统软件公司,还是统计学生学分绩点那个需求,要做排名还有数据导出,当时好像印象中超时时间是7秒还是几秒,然后学生的成绩数据在视图里有40几万吧,但超过一千个学生就各种慢了,反正要1-2分钟时间的样子,当时是死活没有解决办法,我就向项目经理提议干脆写个存储过程吧?一改或存成绩就计算后存到单独的结果表里,不要实时计算了(抱歉当时自己还没有定时任务的概念,简直太差了自己),然后小姐姐还是看了一眼就说:别搞这么复杂,前端做限制最多只能导出1000学生就可以了。
好吧,问题解决了---》能用设计解决的问题别用技术为难自己,这跟后面一个坑几乎是一样的。
3,第二家公司终于像点样子了,阿里系的互联网公司,分布式架构,Dubbo,SpringBoot,Velocity,react全家桶, .....等等各种技术像万花筒一样展现在我的面前,让我意识到自己是多么落后和孤陋寡闻,我像海绵一样在不断学习,不断接触最新技术。希望自己能赶上别的程序员直到来到现在的公司。当时做的一个数据上传系统,我记得产品和开发经理讨论的时候差点打起来了,为了解决数据一致性和服务降级,做了类似2一样的设计--》全局锁(这怎么说呢?当我看到石墨文档可以几个人同时编辑一个文档的时候我都惊呆了,但后来发现一保存就数据丢失,只保留某个人的提交数据。果然没有这么简单,想想连阿里都没办法解决这个问题也是没谁了,编辑文档时锁定只能一个人编辑,其他人没法编辑果然才是最后的解决办法,当然也可能是我太差,眼界不高吧)
4,上述说到现在公司,现在的公司是一家互联网创业企业,当初是被SpringCloud等技术吸引来的,进来后才发现果然人外有人,天外有天,你能想象毕业两年的架构师吗,能想象人家10年前就接触领域驱动设计了吗?能想象人家光书都买了好几千的吗?看看自己两年连本《Thinking in Java》都没看完,至今连一本书都没买过,都是蹭老婆的大学教材,什么《数据库结构》,《Java面对对象编程》之类的草草看看就不禁惭愧不已。于是一咬牙趁着京东半价买了200多的书,发奋学习。。。好吧跑偏了,回到正题。这次是一个重构需求,其中的权限控制模块因为我在上家公司写过权限中间件就分配给我了,写crud就花了不到两天时间搞完了,然后就是各种磨产品写业务校验,花了两个星期终于差不多交工了,突然问了一句权限范围做了没?(其中有个部分负责人角色要求只能看自己部门的信息)瞬间脑仁疼了,我这权限模块倒是没这权限范围问题,其他模块就蛋疼了啊,然后又是花了一天时间讨论,产品最后妥协给出了角色写死,不做权限范围控制的方案。这次真是的血的教训啊,延期不说,辛苦写了两个星期的代码瞬间无用了,写本文的时候正是加班改权限模块的时候,刚好看到同事在写简书,于是刚好把自己这些年来踩的几个小坑总结一下,当然水的很,第一次写,若干年后再回过头来看应该也会觉得幼稚吧。
此致
最容易碰到的就是SQL报错然后花半天排查不出来,明明复制SQL在navicat上能执行,但在项目就死活报错,要不就是空格特殊字符这种低级错误,好吧干货没多少,就这里简单记录一下,
1.批量插入时如果不是在代码里指定创建时间而是用now()函数会导致一批数据创建时间一样,进而导致分页按创建时间排序出现错误--》解决办法:代码new Date()生成批处理的创建时间不要用SQL函数now()
2.注意<foreach>标签是否带括号,下面这段代码两个<foreach>标签的区别浪费老子半天时间(这就是复制粘贴的缺点):
UPDATE table
SET order = CASE ID
<foreach item="menu" index="index" collection="menus">
WHEN #{menu.id} THEN #{menu.order}
</foreach>
END ,MODIFY_TIME = now()
WHERE
ID IN
<foreach item="menu" index="index" collection="menus" open="("
separator="," close=")">#{menu.id}
</foreach>
3.开发前一定确认好需求,并考虑后续升级方便再动工,返工是很伤人的
4.数据库乐观锁一定要有异常解锁机制,别造成锁一直解不掉的问题
5.出于方便部署后查看服务状态的考虑,可以加上test方法,部署好访问test接口返回HelloWorld就好,上线前删掉
6.swagger文档上线一定要关掉
7.如果想直接使用接口功能不用前端的话可以用swagger的接口调试功能实现
8.只要能远程生产服务器(堡垒机或直接远程都可以),就不用再开向日葵(国产远程桌面软件,非常好用,强烈推荐,再不用担心恶心的TeamViewer报检测为商业用途了)或TeamViewer去上传jar包到邮箱再上传服务器这么恶心了,随便上传到OSS设为公共读后将url在远程命令页面wget一下就把jar之类的资源上传到生产服务器了,看数据库也可以用命令操作,具体看另一篇文章。
9.需要https最好是买个负载均衡(不贵几十块钱)配在负载均衡上然后转发到后端服务,不要用网关来代替,非常蠢,很多坑
10.Feign调用接口时,返回值没有也最好用Result<Void>指定返回对象类型而不是Result或Result<?>,因为fastjson有坑会报错,具体参考这篇博文:fastjson的坑
11.需要实时更新前端数据的最方便快速的做法是前端轮询调用接口,而不是用websocket,坑很多,看到同事用websocket一堆bug真是怕了
12.不要一味追求新技术,新技术代表的是新特性是很好用但文档少,后期维护不方便等也要做好准备,就像webfulx,一堆坑,同事发现解决不了后又换回springMVC了,很多技术往往不是不好用而是不会用,所以在确定自己能用好前不要在生产环境用自己不熟悉的新技术,学习成本很高的
13.git文件是隐藏的,所以需要将复制的代码重新上传到个新项目的话要把Windows隐藏文件夹查看打开删掉.git文件夹再重新上传git
14.调用第三方系统时,最好通过接口实现而不是直接改库,有很多暗坑
15.注意mybatis调用返回对象的构造函数,如果有空构造函数会返回一堆null对象导致npe,如果SQL返回没有构造函数的参数会报
No constructor found in (# MyBatis小问题(1)-Mapper中错误No constructor found...
)
16.用swagger UI 做页面写了一个上传的接口给别人用,过了一段时间居然报这个错:
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-datarequest failed. /tmp/tomcat.4251146331506026853.80/work/Tomcat/localhost/ROOT/upload_5b3f21c2_d1a2_4984_8ee6_25e0f1e51be4_00000002.tmp (No such file or directory) at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:165) at org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:142) at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1099) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:932) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:105) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:107) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:522) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1110) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:785) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1425) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) Caused by: org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed./tmp/tomcat.4251146331506026853.80/work/Tomcat/localhost/ROOT/upload_5b3f21c2_d1a2_4984_8ee6_25e0f1e51be4_00000002.tmp (No such file or directory) at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:351) at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:115) at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:158) ... 46 more Caused by: java.io.FileNotFoundException: /tmp/tomcat.4251146331506026853.80/work/Tomcat/localhost/ROOT/upload_5b3f21c2_d1a2_4984_8ee6_25e0f1e51be4_00000002.tmp (No such file or directory) at java.io.FileOutputStream.open0(Native Method) at java.io.FileOutputStream.open(FileOutputStream.java:270) at java.io.FileOutputStream.<init>(FileOutputStream.java:213) at java.io.FileOutputStream.<init>(FileOutputStream.java:162) at org.apache.commons.io.output.DeferredFileOutputStream.thresholdReached(DeferredFileOutputStream.java:178) at org.apache.commons.io.output.ThresholdingOutputStream.checkThreshold(ThresholdingOutputStream.java:224) at org.apache.commons.io.output.ThresholdingOutputStream.write(ThresholdingOutputStream.java:128) at org.apache.commons.fileupload.util.Streams.copy(Streams.java:107) at org.apache.commons.fileupload.util.Streams.copy(Streams.java:70) at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:347) ... 48 more
1、检查bean:
<!-- 配置上传文件的组件 id的值必须为“multipartResolver” -->
<!-- class spring-web org.springframework.web.multipart.commons -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 配置最大的上传文件10兆 -->
<property name="maxUploadSize" value="10000000"></property>
<!-- 设置默认的编码格式 -->
<property name="defaultEncoding" value="utf-8"></property>
</bean>
2、检查表单form设置
<form id="form1" method="POST" enctype="multipart/form-data">
3、控制器方法
@RequestMapping(value = "/upload.shop",headers = "content-type=multipart/*", method = RequestMethod.POST)
4、检查相关jar包
commons-fileupload/io.jar
根据多方查找,找到了原因,原来是Linux下会自动清除tmp目录下10天没有使用过的文件,SpringBoot启动的时候会在/tmp目录下生成一个Tomcat.*的文件目录,用于"java.io.tmpdir"文件流操作,因为放假期间无人操作,导致Linux系统自动删除了临时文件,所以导致上传报错
解决方案
一、临时方案(快速解决方案,但是不推荐)
重启服务,重启后被删除的tomcat目录会重新生成
二、添加启动配置项(如果你的应用是用脚本启动的,可以考虑)
启动时增加参数-Djava.io.tmpdir=自定义目录
三、添加文件配置类(添加配置类,可以考虑)
@Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setLocation("/var/tmp");
return factory.createMultipartConfig();
}
四、修改tmpwatch 删除文件的逻辑(修改了系统,不推荐)
tmpwatch 删除文件的逻辑
五、修改application.yml配置文件(推荐)
设置文件临时存储路径
spring.http.multipart.location = /var/tmp
原文地址:https://somta.com.cn/#/blog/view/ef507e4e6e28434d9787ec715d406491
17.sql报错:
org.springframework.jdbc.UncategorizedSQLException:
Error updating database. Cause: java.sql.SQLException: sql injection violation, syntax error: ERROR. token : WHERE, pos : 315
SQL多了一个逗号;
关键看token后面,这里通过token可以直接定位报错地点;