maven install 是先进行打包,在target目录下生成新jar,同时将打包结果放到本地仓库的相应目录中,供其他项目或模块引用。
=====================================
9月17日
因为Map这个类没有继承Iterable接口所以不能直接通过map.iterator来遍历,所以就只能先转化为set类型,用entrySet()方法,其中set中的每一个元素值就是map中的一个键值对,也就是Map.Entry<K,V>了,然后就可以遍历了。
也就是说:每个entry就是一个键值对,存在set里面
xcode-select –-install 解决mac没有编译器的问题
Java中正则表达式
[]代表匹配中括号中其中任一个字符,如[abc]匹配a或b或c。
-在中括号里面和外面代表含义不同,如在外时,就匹配-,如果在中括号内[a-b]表示匹配26个小写字母中的任一个;[a-zA-Z]匹配大小写共52个字母中任一个;[0-9]匹配十个数字中任一个。
^在中括号里面和外面含义不同,如在外时,就表示开头,如^7[0-9]表示匹配开头是7的,且第二位是任一数字的字符串;如果在中括号里面,表示除了这个字符之外的任意字符(包括数字,特殊字符),如[^abc]表示匹配出去abc之外的其他任一字符。
.表示匹配任意的字符。
+表示出现1次或多次。
为了防止别人拿到链接,重复调用接口的问题(重放问题),我们需要保证请求的唯一性,也就是保证这条请求只能调用一次,不能被重复调用。怎么做呢? ==》 在请求参数中加入时间戳 timestamp(yyyyMMddHHmmss),并一起进行MD5加密签名,
考虑到客户端与服务端之间的网络延迟问题,我们不能保证客户端与服务端的时间完全一致,所以我们可以折中一下:只要满足客户端请求的时间 timestamp 与服务端接收到请求的当前时间 currentTime 之间相差的绝对值在60s以内,我们就算是一次唯一不重发的请求。
这种靠添加时间戳 timestamp 保证请求唯一性的方式可以说已经很好地防止了重发问题的发生;但是假如有高手能够在1分钟之内获取到完整链接,那么他就能够再次利用相同的链接进行重发操作,获取服务端返回的数据!!!
=================================
9月20日
当i=4,j=3时,此时combination有了变化,、
即值为7的时候,j遍历到4的位置,有了另一种组合
所以在7的位置上,有combination+=combination[j]种组合
CyclicBarrier 与 CountDownLatch 区别
- CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
- CountDownLatch 参与的线程的职责是不一样的,有的在倒计时,有的在等待倒计时结束。
- CyclicBarrier 参与的线程职责是一样的
1 | public CyclicBarrier(int parties) |
Executors.newSingleThreadExecutor()这种的实现方式,看名字我们就应该知道是使用单线程重来支持的
JAVA栅栏和闭锁的区别
闭锁: 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。即一组线程在等待某一事件的发生或者得到某些线程的资源。事件没有发生前,所有线程将阻塞等待,而事件发生后,所有线程将开始执行;闭锁最初处于封闭状态,
闭锁CountDownLatch唯一的构造方法CountDownLatch(int count),当在闭锁上调用countDown()方法时,闭锁计数器-1,当闭锁计数器为0时,闭锁将打开,所有线程将通过闭锁开始执行
栅栏:一个同步辅助类,允许一组线程互相等待,知道到达某个公共屏障点。利用栅栏,可以使线程相互等待,直到所有线程到达某一点,然后将栅栏打开,CyclicBarrier支持一个可选的Runnable参数。当线程通过栅栏时,runnable对象将被调用。构造函数CyclicBarrier(int parties, Runnable barrierAction)
当线程在cyclicBarrier对象上调用await()方法时,栅栏的计数器将增加1,当计数器的值为parties时,栅栏将打开。
区别:闭锁用于所有线程等待一个外部事件的发生;栅栏则是所有线程相互等待,直到所有线程都到达某一点时才打开栅栏,然后线程可以继续执行。
闭锁在等别人完成才能冲,栅栏是等其他人到了在一起冲。
canal意思是管道,主要用途是基于Mysql数据库增量日志解析,提供增量数据订阅和消费
ES不支持事务,把ES和关系型数据库结合起来使用,把对数据的增删改放在RDB关系型数据库中执行,然后把这些动作同步到ES。就是利用关系型数据库的ACID事务支持。
以Mysql为例,如果要把数据从同步到ES。canal+binary log就是常用的一种增量解决方案
1 | INSERT INTO 表名(字段名,id,name...) VALUES (..,..,..,); |
adapter 适配 将管道的数据导出到相应的数据库
项目中遇到过这样一个问题,repository继承了CrudRepository接口,直接使用save(S entity) 方法进行数据保存,但是因为某个字段的唯一约束冲突了,导致保存失败并抛出了异常,但是save方法后的代码逻辑却执行了,将数据保存到redis,这导致了数据库和redis数据不一致。代码代码大概是这样子:
1 |
|
然后对这个操作进行了debug,发现到save方法结束,是没有抛出异常的,然后继续进行保存redis等操作,直到方法结束才抛出了异常。
这时注意到了@Transactional注解加在了这个方法之上,那就是事务提交时才会报出 唯一约束冲突的异常,
再联想到Spring data Jpa的是用Hibernate实现的 , Hibernate是有缓存机制的,
猜想不使用jpa自带的save方法,就可以在保存时直接抛异常,而不执行之后的代码,然后进行尝试,的确如此;
我们使用@Transactional 注解时,JpaTransactionManager会在开启事务前打开一个session,
将事务绑定在这个session上,事务结束session关闭,所以后续内容将以粗略以事务作为一级缓存的生存时段。
二级缓存又称为“SessionFactory的缓存”,由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略。第二级缓存是可选的,是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件,二级缓存应用场景局限性比较大,适用于数据要求的实时性和准确性不高、变动很少的情况,此次我们仅针对一级缓存进行详细说明。
我们使用CrudRepository.save() 方法保存或更新对象的流程如下
从上图可以看出每次save方法执行时都会用主键向数据库发起一次查询,来判断是更新还是插入,
此时spring data jpa 不会立马向数据库发送命令,
而是将这条数据保存在一级缓存之中,然后返回缓存中实体对象,
接下来继续执行后续的代码。
如果想更新这条数据的值,可以直接修改这个实体对象,会修改缓存中的数据吗?
jpa会在事前提交之前的某个点(具体后面会说明)自动将这些变更的数据保存至数据库,
并且在事务期间查询这条数据都是优先从缓存中获取数据。
一级缓存的作用还是很明显的,在整个事务中,在对同一条数据进行了保存更新查询操作都会以尽量少地请求数据库的方式进行优化,降低了网络io开销。
有利就有弊,就像第一部分描述的,因为延迟提交 ,数据的正确性验证(数据库限制方面,比如约束)并没有立马执行,有时候完全是我们不能承受的,我们想要的效果并不是这样。
实体对象加入缓存后,我们写sql更新数据,再用自己的sql获取这条数据,得到的是缓存中的数据还是更新后的数据
还是缓存中的数据
总结
Spring data jpa 的这些操作都是简单常用而又容易忽视的,我们在使用时要考虑一下是否得当。
对于这样的缓存机制我们要做的是 将事务控制在合适的范围,将不需要在事务中执行的内容就移出去;
在需要sql明确执行好的情况,就主要避开使用会延迟提交的方法。
@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
1 |
在使用springboot写aop的时候,有个JoinPoint类,用来获取代理类和被代理类的信息。
这个文章记录一下JoinPoint的getSignature方法返回的是什么格式。
Mac强制关机指令
sudo shutdown -r now
在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令:
grep test *file
ps -ef | grep pid 杀进程
10/8日
记录一下自己写的Httpclient获取Json
1 | JSONObject jsonResult=null; |
但是匹配 value 和删除 key 在 Redis 中并不是一个原子性的操作,也没有类似保证原子性的指令,所以可能需要使用像 Lua 这样的脚本来处理了,因为 Lua 脚本可以 保证多个redis指令的原子性执行