最近使用mongDb的时候,出现了一些问题。
mongoDb 实体id自增
在保存实体的时候报了这个错
Cannot aotogenerate id of type java.lang.Long for entity
即 不能自动生成id
之后就带着报错去谷歌了一下。 得到了如下结果:
Mongo ObjectIds 与 jave Long 类型不匹配。
先来介绍一下MongoDB 的对象 Id(ObjectId)
ObjectId
ObjectId 是一个12字节 BSON 类型数据,有以下格式:
前4个字节表示时间戳
接下来的3个字节是机器标识码
紧接的两个字节由进程id组成(PID)
最后三个字节是随机数。
例如 62ee651f cabe7f 59dc f10372
从 MongoDB 3.4 开始(最早发布于 2016 年 12 月),ObjectId 的设计被修改了,中间 5 字节的值由原先的“机器标识码+进程号”改为单纯随机值。
a 4-byte value representing the seconds since the Unix epoch,
a 5-byte random value, and
a 3-byte counter, starting with a random value.
MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。
在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。
MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个 服务器上同步自动增加主键值既费力还费时。
找出原因
之后就在实体定义中发现, 我定义的id是Long类型。
由于mongoDb自动进行id自增的时候,生成的id并不能转换为Long,所以报了错。
@JsonIgnoreProperties(ignoreUnknown = true)
@Document
public class System {
@Id
@JsonView
private Long id;
}
之后改为String 类型之后报错消失。
在数据库中查看数据,查看到的是ObjectId类型。
传到前台,是string类型的数据。
总结
在mongoDb中想要让自动设置id的话,需要设置@Id注释,并设置为string类型。
选择使用 String 属性作为 ID,那么 Mongo 会在保存时自动为其赋值(如果它是空的)。通过选择 String,您可以获得一个由数据库管理的自动分配的 ID,而不必费心手动设置该属性。
但是目前的项目中,id都是从gitlab获取的数据,所以不需要自动设置id,若采用Long类型,并自己设置id ,得到数据就是这样, 表示 id为22。
MongoDB 持久化实体映射
在网上查了有关实体注释时,遇到了一问题。
spring-data-mongodb主要有以下常用的注解.
@Id
主键,不可重复,自带索引
@Document
标注在实体类上,类似于hibernate的entity注解,标明由mongo来维护该表
@Document(collection="mongodb 对应 collection 名")
@Document
public class System {
@Id
@JsonView
private String id;
跟以前不同的是,不用@Entity注解, 用@Document注解。
@Field : 属性
存储到数据库中的字段名。
加这个注解,就是以注解的值对应mongo的key
不加这个注解,默认以参数对应mongo的key
@Field("user_name")
private String userName;
另外@CompoundIndex 联合索引和@Transient:属性就不详细说了。
关联实体
一个比较重要的点就是关联实体。
在JPA中, 我们可以用@ManyToMany,@OneToMany等注释,生成中间表等操作。
但是在mongo中,这些注释貌似并不起作用。
我在尝试添加@ManyToMany后,并没有生成中间表
@DBRef
注解
而在mongo实体中,类似@OneToMany等效果的注解是: @DBRef
这个注释表示关联另一个document对象。类似于mysql的表关联。
没有加该注释的情况下,是这样子。作为普通的数据存到labels里。
加了注释的情况,是这样。
这里拿了其他文章的测试结果,我还没测试
可以发现而是只保存了它的id和namespace. 相当于一个引用,或者说是外键。
另外值得注意的是,
这个注解并且不会处理级联保存,你必须单独先保存@DBRef关联的对象。
你需要先respority.save(@DBRef关联的对象),然后再保存原来的对象。
这是其他文章的测试结果:
小结:
看上去mongo处理的方式有点不同。但是目前项目中虽然使用了mongo,但是还在使用@Entity以及 @ManyToMany等注解,也没有发生什么报错。
但是从结果来看,@ManyToMany没有生成中间表。对mongo没有用。
restTemplate请求重试
在后台向gitlab请求数据的时候,无论尝试了几遍,都会报这个错误。但是别人却能请求。
SSL 异常关闭了。
在之前也报过这个错,网上给出了解决方法一般都是如下。设置一下https协议类型。
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,SSLv3");
但是尝试了几次都没有效果。
之后查看代码,需要获取35个issue下的评论。 猜想到可能是因为请求次数和数据量相关。
于是就尝试了,当restTemplate请求失败时,增加重试机制。
1,安装配置
编辑 pom.xml 文件,添加 相关依赖
<!-- 重试机制 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
2.加入 @EnableRetry 注解
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableRetry
@EnableAsync
public class NonlycodeApplication {
3. 使用@Retryable注解
@Override
@Retryable(value = {RestClientException.class, EOFException.class}, maxAttempts = 3,
backoff = @Backoff(delay = 1000L,multiplier = 1))
public <T> List<T> httGetWithPageRequest(String url, Map<String, String> variables, Integer latestPage, Class<T[]> valueType) throws JsonProcessingException {
@Retryable 注解的方法在发生异常时会重试,参数说明:
value:指定重试异常
maxAttemps:最大重试次数,默认 3
backoff:重试等待策略,默认没有@Backoff 注解为重试等待策略,参数说明:
delay:指定重试的延时时间,默认为 1000L ,1秒
multiplier:指定延迟的倍数,默认为 0。比如 delay=5000l,multiplier=2 时,第一次重试为 5 秒后,第二次为 10 秒,第三次为 20 秒。
这样就能在调用这个方法的时候,只要其中某个异常发生时,被注解的方法就会进行重试。
由于 Retryable 用到了 aspect 增强,所以会有代理类的坑,就是方法内部调用,会使注解失效。
抛出异常
当超过重试次数是异常被抛出。
//发起请求
RestTemplate restTemplate = new RestTemplate(new HttpsClientRequestFactory());
ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.GET, new HttpEntity<String>(headers), String.class, variables);
// 判断请求是否发生异常
if(!response.getStatusCode().is2xxSuccessful()){
System.out.println("请求失败...");
// 抛出异常
throw new RestClientException(response.getBody());
}
我们可以定义一个全局异常处理类。接收抛出的异常,返回给前台。
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(RestClientException.class)
public ResponseEntity<String> throwRestException(RestClientException restClientException){
return new ResponseEntity<String>(restClientException.getMessage(),
HttpStatus.BAD_REQUEST);
}
}
经过测试之后,运行正常,成功进行完所有的请求。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。