缓存击穿,是指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。在高并发下,当第一个线程将数据放入缓存中之前,会出现多个线程从数据库中进行查询的现象,即缓存穿透
解决方法:双重判断加同步代码块
固定语法:
if(第一次对值进行判空,为空时继续){
synchronized (this){
对值进行更新
if(第二次对值进行判空,为空时继续){
从数据库中获取值
}
}
}
一个小例子:
public Integer queryAllUserCount() {
BoundValueOperations<Object, Object> ops = redisTemplate.boundValueOps(Const.ALL_USER_COUNT);
Integer allUserCount = (Integer) ops.get();
if (!ObjectUtils.allNotNull(allUserCount)) {
synchronized (this) {
allUserCount = (Integer) ops.get();
if (!ObjectUtils.allNotNull(allUserCount)) {
allUserCount = userMapper.queryAllUserCount();
ops.set(allUserCount);
}
}
}
return allUserCount;
}
线程池是提供一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁的额外开销,提高了响应的速度。
线程池的体系结构:
java.util.concurrent.Executor 负责线程的使用和调度的根接口
|--ExecutorService 子接口: 线程池的主要接口
|--ThreadPoolExecutor 线程池的实现类
|--ScheduledExceutorService 子接口: 负责线程的调度
|--ScheduledThreadPoolExecutor : 继承ThreadPoolExecutor,实现了ScheduledExecutorService
Executors是一个工具类,用于创建线程池 :
newCachedThreadPool
:用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务。newFixedThreadPool
:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制。newSingleThreadExecutor
:创建一个单线程的线程池,适用于需要保证顺序执行各个任务,并且在任意时间点,不会有多个线程是活动的场景。newScheduledThreadPool
:可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。newWorkStealingPool
:创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行ExecutorService 对象的一些方法:
submit()
:
call()
方法,返回Futurerun()
方法,返回Future,shutdown()
:
简单的一个处理:使用定时器去扫描掉单,然后再处理,使用锁解决定时器和支付同时运行的情况(在支付时(创建了订单但未支付完成),定时器同时扫描到了该条数据,使用锁解决冲突)
select cast(avg(age) as decimal(10,2)) as avgAge from student
一个查询语句就是一个完整的业务逻辑,当前台的响应要获取多个不相关的数据值(不同表或者没有关系的数据)的时候,在Controller层要多次调用相应的Service层,分别查出这些不相关的数据。
在Controller层,对于DML,要调用完整的业务,有时候是多个DML语言是一个业务。这时候给方法起的名字一般在控制层是业务名称,在业务层再细化成各个操作的名字
MyBatis逆向工程生成的 mapper.xml 中使用了
标签,如果查询对应的实体类,则在 标签中使用 resultMap属性指定与实体类的映射关系,因为可能存在表中的字段与实体类的字段不同的情况
Dao层方法的名字要细化,以便方法可以复用
java中使用Date对象往MySQL数据库中存储 Datetime 类型的日期,总是少加个时区。
原因:指定数据库的url时,有个参数serverTimezone
是用来指定时区的,而我写的是UTC(世界标准时间),将UTC改为CTT
(Asia&Shanghai)即可。
spring.datasource.url=jdbc:mysql://localhost:3306/p2p?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CTT
可以在含有外键的表对应的实体类中,添加外键对应字段,该字段是外键对应的表的实体类
例如:
学生表和班级表:学生表中存放有班级表的外键,当多表联查时,可以在学生类中添加一个班级类的字段,在mapper.xml文件中,使用
标签和
标签来查出的值与实体类字段进行映射
<resultMap id="BaseResultMap" type="com.wkcto.springboot.model.Student">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="age" jdbcType="INTEGER" property="age"/>
<association property="classRoom" javaType="com.wkcto.springboot.model.ClassRoom">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
association>
resultMap>
这两个函数的功能是等价的,都是在页面加载完毕后再执行
$(function(){})
//这两个方法是等价的,都是在页面加载完毕后,然后执行
$(document).ready(function(){}) //在文档加载后激活函数
当html标签的属性和JS中均定义了某个相同的事件,则只会触发属性中定义的事件
使用MyBatis逆向工程,推荐字段中分隔单词使用下划线"_
",则逆向工程生成的实体类,会自动把下划线去掉,并转换为驼峰命名法,因此,要使用
标签来将表中的字段与实体类中的字段一一映射起来
实现:基于数据版本(Version)记录机制实现
具体可通过给表加一个版本号或时间戳字段实现,当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断当前版本信息与第一次取出来的版本值大小,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据,拒绝更新,让用户重新操作。
current_date:只显示日期,2020-01-01
current_time:只显示时间,23:11:49
current_timestamp:显示日期加时间,2020-01-01 23:11:49
常用的正则表达式:https://editor.csdn.net/md?articleId=103735776
在JS中使用正则表达式:
/正则表达式/.test(字符串)
,如果匹配则返回true,否则返回falsejquery 的 each 方法中如果使用了 return true
或者 return
相当于是 continue
,而 return false
相当于是 break
。并不会使方法结束
$().text()
会将$() 中所有取出的DOM元素中的文本内容进行拼接,而其它的方法如 $().html()
只会对第一个DOM元素进行操作
$("div[class$=Err]")
:选取所有class属性以Err结尾的div标签$("div[class^=pro]")
:选取所有class属性以pro开头的div标签$("#input").onblur(function(){});
$("input").blur();
var password = $.md5(loginPassword) //返回加密后的密码
在IDEA中进行debug时,因为发生了超时,dubbo自动进行重试,会多次发送请求,如果刚好是往数据库中插入数据,而且还不是在Service层的事务中,那么可能会发生往数据库中重复插入多条相同的数据问题(主键自增)
使用数据库乐观锁解决
生成订单,如果不付款,会在一定时间内过期 ,比如说在15分钟内过期,但是如果是在23点55分生成的订单,则会在 0 点的时候过期,即时间并不够15分钟
将要随机生成的元素存放在数组中,可以是任何元素,然后使用Math.Random()*array.length
来随机生成数组的下标
注意:java中默认小数转为整数是去掉小数点后的部分,使用Math.round(num)
可以将小数四舍五入进行取整
在IDEA中使用TODO来记录自己还没有完成的功能
脱敏操作是指:在页面中,将敏感的数据变得不敏感,比如金额,手机号等,使用***
来对原数据进行一定修改,进行脱敏操作
各种语言通用的一种思路:将小数乘以100,再取整,再将整数除以100
java:使用DecimalFormat对象
0:代表一个数字,如果不存在显示0
#:代表一个或多个数字,如果不存在则显示为空
DecimalFormat decimalFormat = new DecimalFormat("#.00");
Double d = Double.valueOf(decimalFormat.format(13.13521));//
js:toFixed(四舍五入保留的位数):
var num = new Number(12.3863);
document.write(num.toFixed(2));//输入出:12.39
主要方法:
add(field, amount)
:
public static Date getDateByAddDays(Date date, Integer count) {
//日期处理类对象
Calendar instance = Calendar.getInstance();
//设置日期处理类对象的日期值
instance.setTime(date);
//在指定日期上添加天数
instance.add(Calendar.DATE, count);
return instance.getTime();
}
直接使用 DateUtils 的相关函数即可,例如:
DateUtils.addDays(Date date, int amount)
:在给定的日期上加上指定的天数DateUtils.addMonths(Date date, int amount)
:在给定的上期上加上指定的月数
标签指定文件的位置,一般为system
标签指定本地jar包的位置${basedir}
是项目的根目录<dependency>
<groupId>com.alipay.sdkgroupId>
<artifactId>alipay-sdk-javaartifactId>
<version>1.0version>
<scope>systemscope>
<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/alipay-sdk-java20170324180803.jarsystemPath>
dependency>
String s1 = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
Long l2 = redisTemplate.opsForValue().increment("onlyNum", 1L);
String result = s1 + l2;
要会看时序图,会画时序图,对于复杂的业务逻辑,可以画个时序图,使用Rational工具
实线是请求,虚线是响应
ctrl + d 是删除组件
从后台访问其它网站的页面,比如说支付宝的页面,可以使用请求转发,但是请求转发只能是get请求方式,请求参数都在地址栏中,这时候可以采用如下的方式:
例如:
<form method="post" action="http://localhost:9094/pay/api/alipay">
<input type="hidden" name="out_trade_no" th:value="${out_trade_no}">
<input type="hidden" name="total_amount" th:value="${total_amount}">
<input type="hidden" name="subject" th:value="${subject}">
form>
<script>document.forms[0].submit()script>
随机生成的验证码存放在redis中,键是手机号,值是验证码
主要的实现过程:https://blog.csdn.net/zyx1260168395/article/details/103747807
当只更新某个字段时,实体类中只需传入需要更新的字段即可,即便是实体类中的字段的值和数据库中的值相同,没必要将其传入,因为虽然值一样,但还是更新了一遍数据库,速度慢
比如认证时要更新用户的姓名和身份证号
思路一:从可以用从Session中取出的user(含有许多信息),加入身份证号和姓名,再传入dao层进行更新,但是虽然只用到了实体类中的id,姓名和身份证号,但是user表中其它的字段也更新了一遍,会造成速度缓慢,所以使用思路二
思路二:新建一个User,只加入需要改的字段即可
思路:跳往登录页时,在前端获取当前页的网址,当作请求参数传递到后台,然后跳到登录页后,存在登录页的一个隐藏域中,当登录成功时,获取到这个隐藏域中的地址,再跳到这个地址
获取当前页的网址(Thymeleaf):
主要属性:
#httpServletRequest.requestURL
:获取当前页面的URL#httpServletRequest.queryString
:获取当前页面的请求参数,为null时省略var redirectUrl = [[${#httpServletRequest.requestURL + (#httpServletRequest.queryString == null? "": "?" + #httpServletRequest.queryString)}]];
//这两种方法均可
var redirectUrl = [[${#strings.replace(#httpServletRequest.requestURL + '?' + #httpServletRequest.queryString,"?null","")}]];
使用redis中的zset集合(zset可以用来解决各种排行榜问题),key为电话号码,score为累计投资金额,用户每投资一笔,就往score中加一笔。
使用到的方法:
incrementScore(K, V, delta)
:元素分数增加,delta是增量
rangeWithScores(K,start,end)
:键为K的集合,索引start<=index<=end的元素子集,返回泛型接口(包括score和value),正序
reverseRangeWithScores(K,start,end)
:键为K的集合,索引start<=index<=end的元素子集,返回泛型接口(包括score和value),倒序
ZSetOperations 操作解释:https://www.cnblogs.com/pqy521/p/7009620.html
要学会看API文档,支付宝文档:https://docs.open.alipay.com/catalog
@RequestMapping(value = "/jcaptcha/captcha")
public void handleCaptchaRequest(HttpServletRequest request, HttpServletResponse response) {
//生成6位随机验证码,这里就简单的使用一个固定的字符串代替了
String captcha = "1Ag5Kw";
try {
//创建字节数组输出流
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
//创建图片缓存对象,BufferedImage.TYPE_INT_RGB : 表示一个图像,该图像具有整数像素的 8 位 RGB 颜色
BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
//获取图片的画布
Graphics graphics = bufferedImage.getGraphics();
//设置画布背景色
graphics.setColor(Color.GREEN);
//设置画布填充区域
graphics.fillRect(0, 0, WIDTH, HEIGHT);
//边框区域
graphics.drawRect(1, 1, WIDTH - 2, HEIGHT - 2);
//设置字体颜色
graphics.setColor(Color.red);
//设置字体样式
graphics.setFont(new Font("微软雅黑", Font.ITALIC, 32));
//填充数据
graphics.drawString(captcha, 10, 38);
//将生成的验证码存放到session中
request.getSession().setAttribute(Constants.CAPTCHA, captcha);
ImageIO.write(bufferedImage, "jpeg", jpegOutputStream);
byte[] captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
//将验证码输出到页面
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0L);
response.setContentType("image/jpeg");
ServletOutputStream respOs = response.getOutputStream();
respOs.write(captchaChallengeAsJpeg);
respOs.flush();
respOs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
使用的是Google生成二维码的依赖:
<dependency>
<groupId>com.google.zxinggroupId>
<artifactId>coreartifactId>
<version>3.0.0version>
dependency>
<dependency>
<groupId>com.google.zxinggroupId>
<artifactId>javaseartifactId>
<version>3.0.0version>
dependency>
生成二维码:
Map<EncodeHintType,Object> map = new HashMap<EncodeHintType, Object>();
//设置字符编码
map.put(EncodeHintType.CHARACTER_SET, "UTF-8");
/*创建一个二维码,
第一个参数是二维码内容,
第二个参数是常量,二维码的编码规则,
第三、四个参数是二维码的长和宽,
最后一个参数是设置的一些参数
*/
BitMatrix encode = new MultiFormatWriter().encode("https://blog.csdn.net/zyx1260168395", BarcodeFormat.QR_CODE, 200, 200, map);
//将二维码转换成图片,写到指定的路径上
Path path = FileSystems.getDefault().getPath("D://", "grcde.jpg");
MatrixToImageWriter.writeToPath(encode, "jpg", path);
//在浏览器使用输出流来将二维码图片响应到前台
OutputStream out = response.getOutputStream();
MatrixToImageWriter.writeToStream(encode, "jpg", out);
业务层:session;biz
jar包名字中有 source 的是源码包,不带 source 的是编译好的jar包
用例:测试中用的功能点(用例图,测试用例)
商户系统:开发者的系统
渠道、终端:
code:通信标识,10000表示通信成功
请求/响应参数又叫请求/响应报文
timestamp:时间戳
sign:签名
out_trade_no:商户订单号
@RequestMapping("/logout")
public Object logout(HttpServletRequest request) {
HttpSession session = request.getSession();
session.invalidate();
return "redirect:/";
}
当注解中的参数只有一个 value 属性时,可以省略value不写,当注解中有多个参数时,每个参数都必须指定属性名。
将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)
属性:
value
:参数名required
:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。defaultValue
:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认父模版中的build标签中的内容是不能被子模块继承的
使用 fn
标签:https://www.cnblogs.com/evolcq/p/3688443.html?utm_source=tuicool&utm_medium=referral
templates文件夹中的文件是受保护的,必须要通过后台才能进行访问
可变长参数可以传递多个值,也可以传递数组(类型要对)