git使用技巧-撤销修改

git使用技巧-撤销修改

git撤销修改

丢弃工作区的修改

1
git checkout  --文件名称

把暂存区的修改撤销掉(unstage),重新放回工作区

1
git reset HEAD 文件名称

已经commit到版本库,想撤回本次commit

前提:没有推送到远端

1
git reset --hard HEAD^

或者

1
git reset --hard commit_id

分布式id生成-雪花算法实现资源汇总

分布式id生成-雪花算法实现资源汇总

讲分布式Id生成的文章很多,其中雪花算法也提到过多次,本文不再赘述,只是给出资源汇总,仅供参考。

snowflake-snowflake-2010

1
twitter原版,scala编写,地址:https://github.com/twitter-archive/snowflake

java版本snowflake

代码源地址 参见

Leaf——美团点评分布式ID生成系统

1
2
介绍参见Leaf——美团点评分布式ID生成系统 https://tech.meituan.com/2017/04/21/mt-leaf.html
代码参见https://github.com/Meituan-Dianping/Leaf

mybatis xml常用写法-传入数组list

mybatis xml常用写法-传入数组list

需求:xml中传入参数中包含一个list,需要在where中拼接in语句

假设查询person表,参数类型为XXXVo,XXXVo中包含一个List对象,保存了状态列表,此时可以参考如下查询

1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="queryXXX" parameterType="XXXVo"
resultMap="XXXResult">
select *
from person
WHERE 1=1
<if test="statusFilter != null and statusFilter.size() > 0">
and status in
<foreach collection="statusFilter" item="statusId" index="i" open="(" close=")" separator=",">
#{statusId}
</foreach>
</if>
ORDER BY DEPTID
</select>

通过Map对象传递参数给xml

参数同样可以通过Map对象传递到xml这个层面,此时这样写即可:
java代码:

1
map.put(statusFilter, 列表实例对象);

xml代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="queryXXX" parameterType="java.util.HashMap"
resultMap="XXXResult">
select *
from person
WHERE 1=1
<if test="statusFilter != null and statusFilter.size() > 0">
and status in
<foreach collection="statusFilter" item="statusId" index="i" open="(" close=")" separator=",">
#{statusId}
</foreach>
</if>
ORDER BY DEPTID
</select>

参考文章

mybatis xml常用写法-使用like关键字【整理+转载】

mybatis xml常用写法-使用like关键字【整理+转载】

需求:xml中需要在where中拼接like语句

方法1:concat

1
2
3
4
5
6
7
8
9
10
<where>
<trim suffixOverrides="," >
<if test="id != null and id != ''" >
and id = #{id}
</if>
<if test="name != null and name != ''" >
and name like concat('%',#{name},'%')
</if>
</trim>
</where>

方法2:${}

1
2
3
<if test="examTypeName!=null and examTypeName!=''">
and exam_type_name like '%${examTypeName}%'
</if>

方法3:#{}

1
2
3
<if test="examTypeName!=null and examTypeName!=''">
and exam_type_name like "%"#{examTypeName}"%"
</if>

参考文章

google guava使用教程系列(3)- 前置条件检查

google guava使用教程系列(3)- 前置条件检查

google guava使用教程系列(3)- 前置条件检查

原文地址:[https://github.com/google/guava/wiki/PreconditionsExplained])(https://github.com/google/guava/wiki/PreconditionsExplained)

简而言之,guava提供了一系列检查参数的方案,个人感觉一般,实际业务场景中对于参数判断自己写可能比这种封装更方便。

官方举的例子:

1
2
checkArgument(i >= 0, "Argument was %s but expected nonnegative", i);
checkArgument(i < j, "Expected i < j, but %s >= %s", i, j);

大致有如下几类:

  • checkArgument(boolean)
  • checkNotNull(T)
  • checkState(boolean)
  • checkElementIndex(int index, int size)
  • checkPositionIndex(int index, int size)
  • checkPositionIndexes(int start, int end, int size)

http://ifeve.com/google-guava-preconditions/有翻译,但翻译不全,建议github上的原版wiki。看名字基本能猜到方法用途,看个人喜好吧,我个人倒是更喜欢apache commons里的校验方法,比如StringUtils.isEmpty()

【听课笔记】java分布式锁

【听课笔记】java分布式锁

课程链接

课程:https://url.163.com/VD8

java锁

synchronized

在jdk 1.5以后,优化了,使其性能并不是像很多帖子说的那样,“非常重”

JUC lock

方法 说明
lock() 获取锁,如果锁被暂用则一直等待
tryLock() 如果获取锁的时候锁被占用就返回false,否则返回true
tryLock(long time, TimeUnit unit) 比起tryLock,多出等待时间
unLock()
lockInterruptibly()

lock与synchronized区别

  • lock是接口,syn是java关键字,是内置实现;
  • sync 发生异常时自动释放线程占有的锁,因此不会导致死锁现象发生;lock在发生异常时,如果没有unLock去释放锁,可能造成死锁。因此使用lock时需要在finally中释放锁
  • lock 可以让等待锁的线程响应中断;sync 不行,使用sync等待线程一直等待下去,不能中断
    lock可以知道有没有成功获得锁,sync则无法知道
  • lock可提高多线程读效率

性能上,竞争不激烈,两者性能;竞争激烈时,lock性能远由于sync。具体需要根据情况而定。
并不一定总是需要用lock,sync更方便,lock需要更精心维护代码、避免死锁

分布式锁

push
pull
的方式

惊群效应

分布式锁需要具备的条件:

  • 互斥性
  • 可重入
  • 超时高效
  • 阻塞/非阻塞

几种实现方式:

  1. 数据库实现(乐观锁)
  2. 基于zookeeper的实现
  3. 基于redis的实现
  4. 自研分布式锁(google的chubby, zookeeper实际上是基于这个chubby)

基于数据库实现

基于redis的实现

基本命令

1
SETNX key value   

if key不存在,则设置为value;存在则不做任何操作
“SET if Not eXist”

1
expire key seconds 

设置过期时间,如果key已国企,则将会被自动删除。

1
del key

删除key

实现方式

基本锁

原理:利用redis的setnx,如果不存在某个key则设置值,设置成功则表示锁成功
缺点:如果获取锁后的进程在没有执行完就挂了,则锁永远不会释放

改进型

改进:在基本形式锁上setnx后设置expire,保证超时后也能自动释放锁
缺点:setnx与expire不是一个原子操作,可能执行完setnx该进程就挂了

再改进

lua执行具有原子性
改进:利用lua脚本,将setnx与expire编程一个原子操作,可解决一部分问题
缺点:还是会出现锁过期的问题

具体实现

官方提供的java组件:redisson
redisson的分布式可重入锁RLock java对象实现了java.util.concurrent.locks.lock接口同时还支持过期解锁
地址:

分布式锁方案比较

  • 从理解的难易程度(从低到高)
    数据库 > 缓存 > zookeeper
  • 从实现的复杂性角度
    zookeeper >= 缓存 > 数据
  • 从性能角度(从高到底)
    缓存 > zookeeper >= 数据库
  • 从可靠性角度(从高到低)
    zookeeper > 缓存 > 数据库

扩展

redis的可以用于哪些场景?

  • 缓存
  • 消息队列
  • 分布式锁
  • 发布订阅

spring boot上传附件报错:org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException【转载+整理】

spring boot上传附件报错:org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException【转载+整理】

问题

spring boot + spring cloud,上传附件时遇到如下错误:

1
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (11963927) exceeds the configured maximum (10485760)

错误信息表示上传附件报超出自带tomacat限制大小(默认1M)\

解决

  1. 在配置文件(application.properties或是application.yml)加入如下代码
1
2
spring.http.multipart.maxFileSize = 10Mb
spring.http.multipart.maxRequestSize=100Mb
  1. 把如下代码放在启动类上,并在类上加入@Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 文件上传配置
*
* @return
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
// 单个数据大小
factory.setMaxFileSize("10240KB"); // KB,MB
/// 总上传数据大小
factory.setMaxRequestSize("102400KB");
return factory.createMultipartConfig();
}

我这里选择只修改配置参数,就修复了问题。

参考资料

spring boot整合swagger

spring boot整合swagger

本文仅展示总体配置,具体注解用法请另行搜索、查询。

1.加上maven依赖,引入相关包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
</pre>
<pre><dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-annotations -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.0.8</version>
</dependency>
<!-- http://localhost:18004/doc.html -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.8.8</version>
</dependency>
<!-- Swagger用了高版本的 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency></pre>
<pre>

2.spring boot项目中加入配置类

添加一个配置对象,大体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61


package com.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import io.swagger.annotations.Api;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
* Swagger2配置
* http://localhost:18005/swagger-ui.html
* http://localhost:18005/doc.html
*/
@Configuration
@EnableSwagger2
@Profile({"dev", "pre"})
class SwaggerConfig {
/**
* swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
//为当前包路径
//.apis(RequestHandlerSelectors.basePackage("com.br.demo.controller"))
.paths(PathSelectors.any())
.build();
}

/**
* 构建 api文档的详细信息函数
*/
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfoBuilder()
//页面标题
.title("账务系统接口API文档")
//创建人
.contact(new Contact("xx", "http://xx.100credit.cn", "xx@100credit.com"))
//版本号
.version("1.0")
//描述
.description("账务系统接口大全")
.build();
return apiInfo;
}
}


3.用各种注解加入接口说明

例如controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37


import javax.annotation.Resource;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.demo.bean.model.User;
import com.demo.common.result.Result;
import com.demo.common.result.ResultUtils;
import com.demo.controller.BaseController;
import com.demo.service.UserService;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping("api/user")
@Api("UserController相关的api")
public class UserController extends BaseController {

@Resource
private UserService userService;

@ApiOperation(value = "查询用户", notes = "获得用户信息")
@PostMapping("/getUser")
private Result<User> getUser(User user) {
User userResult = new User();
userResult.setName("xx");
userResult.setEmail("xx@qq.com");
return ResultUtils.succeed(userResult);
}
}

实体类示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


Model模型类

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel("用户实体类")
@Data
public class User {

@ApiModelProperty(name="name",value="用户名",required=true)
private String name;

@ApiModelProperty(name="email",value="邮箱",required=false)
private String email;
}

4. 效果

5. 如果配置了shiro或是拦截器,注意需要打开相关权限

以shiro为例,需做如下配置:

1
2
3
4
5
6
7
8
9


//对swagger2相关接口放行,5个配置
filterChainDefinitionMap.put("/doc.html", "anon");
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger-resources/**", "anon");
filterChainDefinitionMap.put("/v2/api-docs/**", "anon");
filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");

6. swagger接口显示地址

7.参考文档

swagger2 注解说明 ( @ApiImplicitParams )
https://blog.csdn.net/jiangyu1013/article/details/83107255

spring boot项目中使用swagger2
https://www.jianshu.com/p/05be40b9a7a3

spring boot 整合 swagger2,并设置post,get请求方式
https://blog.csdn.net/qq\_36249132/article/details/90109815

java8 快速实现List转map 、分组、过滤等操作【转载】

# java8 快速实现List转map 、分组、过滤等操作【转载】 版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。\ 本文链接:https://blog.csdn.net/lu930124/article/details/77595585 利用java8新特性,可以用简洁高效的代码来实现一些数据处理。 定义1个Apple对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


public class Apple {
private Integer id;
private String name;
private BigDecimal money;
private Integer num;
public Apple(Integer id, String name, BigDecimal money, Integer num) {
this.id = id;
this.name = name;
this.money = money;
this.num = num;
}
}

\ 添加一些测试数据:
1
2
3
4
5
6
7
8
9
10
11
12
13

List<Apple> appleList = new ArrayList<>();//存放apple对象集合

Apple apple1 = new Apple(1,"苹果1",new BigDecimal("3.25"),10);
Apple apple12 = new Apple(1,"苹果2",new BigDecimal("1.35"),20);
Apple apple2 = new Apple(2,"香蕉",new BigDecimal("2.89"),30);
Apple apple3 = new Apple(3,"荔枝",new BigDecimal("9.99"),40);

appleList.add(apple1);
appleList.add(apple12);
appleList.add(apple2);
appleList.add(apple3);

1、分组\ List里面的对象元素,以某个属性来分组,例如,以id分组,将id相同的放在一起:
1
2
3
4
5
6
7


//List 以ID分组 Map<Integer,List<Apple>>
Map<Integer, List<Apple>> groupBy = appleList.stream().collect(Collectors.groupingBy(Apple::getId));

System.err.println("groupBy:"+groupBy);

{1=[Apple{id=1, name=’苹果1′, money=3.25, num=10}, Apple{id=1, name=’苹果2′, money=1.35, num=20}], 2=[Apple{id=2, name=’香蕉’, money=2.89, num=30}], 3=[Apple{id=3, name=’荔枝’, money=9.99, num=40}]} 2、List转Map\ id为key,apple对象为value,可以这么做:
1
2
3
4
5
6
7
8
9
10
11


/**
* List -> Map
* 需要注意的是:
* toMap 如果集合对象有重复的key,会报错Duplicate key ....
* apple1,apple12的id都为1。
* 可以用 (k1,k2)->k1 来设置,如果有重复的key,则保留key1,舍弃key2
*/
Map<Integer, Apple> appleMap = appleList.stream().collect(Collectors.toMap(Apple::getId, a -> a,(k1,k2)->k1));

打印appleMap\ {1=Apple{id=1, name=’苹果1′, money=3.25, num=10}, 2=Apple{id=2, name=’香蕉’, money=2.89, num=30}, 3=Apple{id=3, name=’荔枝’, money=9.99, num=40}} 3、过滤Filter\ 从集合中过滤出来符合条件的元素:
1
2
3
4
5
6
7


//过滤出符合条件的数据
List<Apple> filterList = appleList.stream().filter(a -> a.getName().equals("香蕉")).collect(Collectors.toList());

System.err.println("filterList:"+filterList);

[Apple{id=2, name=’香蕉’, money=2.89, num=30}] 4.求和\ 将集合中的数据按照某个属性求和:
1
2
3
4
5
6


//计算 总金额
BigDecimal totalMoney = appleList.stream().map(Apple::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
System.err.println("totalMoney:"+totalMoney); //totalMoney:17.48

5.查找流中最大 最小值
1
2
3
4
5
6
7
8
9
10
11

Collectors.maxBy 和 Collectors.minBy 来计算流中的最大或最小值。

Optional<Dish> maxDish = Dish.menu.stream().
collect(Collectors.maxBy(Comparator.comparing(Dish::getCalories)));
maxDish.ifPresent(System.out::println);

Optional<Dish> minDish = Dish.menu.stream().
collect(Collectors.minBy(Comparator.comparing(Dish::getCalories)));
minDish.ifPresent(System.out::println);

6.去重
1
2
3
4
5
6
7
8
9
10
11

import static java.util.Comparator.comparingLong;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

// 根据id去重
List<Person> unique = appleList.stream().collect(
collectingAndThen(
toCollection(() -> new TreeSet<>(comparingLong(Apple::getId))), ArrayList::new)
);

下表展示 Collectors 类的静态工厂方法。 工厂方法 返回类型 作用\ toList List 把流中所有项目收集到一个 List\ toSet Set 把流中所有项目收集到一个 Set,删除重复项\ toCollection Collection 把流中所有项目收集到给定的供应源创建的集合menuStream.collect(toCollection(), ArrayList::new)\ counting Long 计算流中元素的个数\ sumInt Integer 对流中项目的一个整数属性求和\ averagingInt Double 计算流中项目 Integer 属性的平均值\ summarizingInt IntSummaryStatistics 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值\ joining String 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(“, “))\ maxBy Optional 一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty()\ minBy Optional 一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty()\ reducing 归约操作产生的类型 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));\ collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))\ groupingBy Map> 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键\ partitioningBy Map> 根据对流中每个项目应用谓词的结果来对项目进行分区 最后一个是表格,可以参见原帖: ![](images/常用collectors-300x204_692499fb.png)

Arrays.asList中所遇到的坑【转载】

Arrays.asList中所遇到的坑【转载】

转载自:https://www.cnblogs.com/wang-meng/p/f1532cf23ce049ce63b4bdd62d53659d.html

前言

最近在项目上线的时候发现一个问题,从后台报错日志看:java.lang.UnsupportedOperationException异常
从代码定位来看,原来是使用了Arrays.asList()方法时把一个数组转化成List列表时,对得到的List列表进行add()和remove()操作, 所以导致了这个问题。

对于这个问题,现在来总结下,当然会总结Arrays下面的一些坑。

源代码分析

首先,遇到问题不可怕,遇到问题解决就是了,但是必须要保证下次不会再犯相同的问题。
Arrays.asList返回的是同样的ArrayList,为什么就不能使用add和remove方法呢?

1,查看Arrays.asList 源码
,

2,查看此ArrayList结构:

3, 在查看AbstractList结构:

果然,UnsupportedOperationException 是这里抛出的,因为Arrays中的ArrayList并没有实现此方法,故抛出了异常。
所以说 Arrays.asList 返回的 List 是一个不可变长度的列表,此列表不再具备原 List 的很多特性,因此慎用 Arrays.asList 方法。

Arrays中其他坑

1,下面程序输出是什么?

打印结果是:1
由上面asList 源码我们可以看到返回的 Arrays 的内部类 ArrayList 构造方法接收的是一个类型为 T 的数组,而基本类型是不能作为泛型参数的,所以这里参数 a 只能接收引用类型,自然为了编译通过编译器就把上面的 int[] 数组当做了一个引用参数,所以 size 为 1,要想修改这个问题很简单,将 int[] 换成 Integer[] 即可。所以原始类型不能作为 Arrays.asList 方法的参数,否则会被当做一个参数。

2,Collections.toArray报错问题
大家可以看下 java.util.ArrayList 源码 中特别标记有一句话如下:

Bug地址:https://bugs.java.com/view_bug.do?bug_id=6260652
那下面来试验下什么情况下会出现这种异常:

如上图,这种控制台打印的结果如下:
class [Ljava.lang.String;
Exception in thread “main” java.lang.ArrayStoreException: java.lang.Object

我们查看Arrays中ArrayList的toArray源码:

因为asList返回的是一个String数组,所以这里toArray返回的其实是String[]类型,只不过是这里做了一个向上转型,将String[]类型转为Object[]类型罢了。
但是注意,虽然返回的引用为Object[],但实际的类型还是String[],当你往一个引用类型和实际类型不匹配的对象中添加元素时,就是报错。
具体大家可以参考Java向上转型和向下转型的相关知识点。

关于Arrays中的坑就说到这里,有补充的欢迎留言。