springboot 与数据接口

springboot 与数据接口

一. 简述

从以往的 Spring 项目开发经验来看,SpringJSON 情有独钟,这也得益于 JSONJS 发明的一种轻量级的数据交换格式,因为本身 JS 是弱类型的语言,所以 JSON 便没有什么特定类型限制,使得其他各门语言都可以对 JSON 进行解析,从而序列化成各自的对象。 其实前面说的已经有一点半点的,基本都是返回 JSON 字符串,所以这里便加深一点,涉及 SpringMVC 可以用于开发的注解。
GitHub:https://github.com/WeidanLi/spring-boot-tutorial 项目示例:web-restful

二. 开发

1. maven 依赖

maven 依赖依然只需要 spring-boot-starter-web 的依赖即可

1
2
3
4
5
6
7
<dependencies>
<!-- 引入 web-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

2. 资源控制器

我这里为了能够说明明白,先假定控制器是用户资源控制器,我可以通过公司查询,也可以通过用户的某个字段查询。 一般来说,一个资源控制器,我喜欢只关注聚合类的资源,比如用户控制器,返回的一般都是用户,操作的也一般是用户。 使用 RESTful 风格来编排 URL ,以前开发的时候因为没有关注到一些注解的用法,RESTful 用起来总是有点吃力,因为有些时候我是从另外一个方向去查询的,并不是 User 的内部属性。 由于先不接触数据库,所以先用集合模拟数据。

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package cn.liweidan.springboot.webrestful.endpoint;

import cn.liweidan.springboot.webrestful.dbo.UserDo;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
* Description:用户资源控制器
*
* @author liweidan
* @version 1.0
* @date 2018/11/22 11:00 AM
* @email toweidan@126.com
*/
@RestController
@RequestMapping("user")
public class UserEndpoint {

/** 公司关联用户ID表 */
Map<Long, List<Long>> orgRelationUser = new HashMap<>();
/** 用户表 */
List<UserDo> userDoList = new ArrayList<>();

public UserEndpoint() {
/**
* 假设五个用户,隶属于两个公司,我可能从公司ID查询,也根据条件查询全部
*/
userDoList.add(new UserDo(1L, "weidan", 6000L, 18));
userDoList.add(new UserDo(2L, "xiaodan", 10000L, 36));
userDoList.add(new UserDo(3L, "dadan", 9000L, 28));
userDoList.add(new UserDo(4L, "Sally", 12000L, 24));
userDoList.add(new UserDo(5L, "weisuodan", 20000L, 20));

List<Long> userIds1 = new ArrayList<>(3);
userIds1.add(1L);
userIds1.add(3L);
userIds1.add(4L);
orgRelationUser.put(10L, userIds1);

List<Long> userIds2 = new ArrayList<>(2);
userIds2.add(2L);
userIds2.add(5L);
orgRelationUser.put(7L, userIds2);
}

@GetMapping(params = "orgId")
public List<UserDo> selectByOrg(@RequestParam("orgId") Long orgId) {
List<Long> userIds = orgRelationUser.get(orgId);
if (Objects.isNull(userIds) userIds.isEmpty()) {
return new ArrayList<>(0);
}
return userDoList.stream().filter(userDo -> userIds.contains(userDo.getId())).collect(Collectors.toList());
}

@GetMapping(params = {"salaryMin", "salaryMax"})
public List<UserDo> selectAllByCondition(@RequestParam(value = "salaryMin", defaultValue = "0") Long salaryMin,
@RequestParam(value = "salaryMax", defaultValue = Long.MAX_VALUE + "") Long salaryMax) {
return userDoList.stream()
.filter(userDo -> userDo.getSalary() > salaryMin && userDo.getSalary() < salaryMax)
.collect(Collectors.toList());
}

@GetMapping
public List<UserDo> selectAll() {
return userDoList;
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void addUser(@RequestBody UserDo userDo, @RequestParam("orgId") Long orgId) {
Long nextId = (long) (userDoList.size() + 1);
userDo.setId(nextId);
userDoList.add(userDo);

List<Long> userIds = orgRelationUser.get(orgId);
if (Objects.isNull(userIds)) {
userIds = new ArrayList<>();
}
userIds.add(nextId);
orgRelationUser.put(orgId, userIds);
}

@PutMapping
@ResponseStatus(HttpStatus.NO_CONTENT)
public void updateUser(@RequestBody UserDo userDo) {
for (UserDo origin : userDoList) {
if (origin.getId().equals(userDo.getId())) {
origin.setName(userDo.getName());
origin.setSalary(userDo.getSalary());
origin.setAge(userDo.getAge());
break;
}
}
}

@DeleteMapping("{userId}")
public void deleteUser(@PathVariable("userId") Long userId) {
userDoList = userDoList.stream().filter(userDo -> !userDo.getId().equals(userId)).collect(Collectors.toList());
}

}

3. 接口测试

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# 根据公司ID查询
GET http://localhost:8080/user?orgId=10

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 22 Nov 2018 08:12:46 GMT

[
{
"id": 1,
"name": "weidan",
"salary": 6000,
"age": 18
},
{
"id": 3,
"name": "dadan",
"salary": 9000,
"age": 28
},
{
"id": 4,
"name": "Sally",
"salary": 12000,
"age": 24
}
]

Response code: 200; Time: 469ms; Content length: 144 bytes
---------------------------------------------------------
# 根据条件查询全部
GET http://localhost:8080/user?salaryMin=9000

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 22 Nov 2018 08:13:38 GMT

[
{
"id": 1,
"name": "weidan",
"salary": 6000,
"age": 18
},
{
"id": 2,
"name": "xiaodan",
"salary": 10000,
"age": 36
},
{
"id": 3,
"name": "dadan",
"salary": 9000,
"age": 28
},
{
"id": 4,
"name": "Sally",
"salary": 12000,
"age": 24
},
{
"id": 5,
"name": "weisuodan",
"salary": 20000,
"age": 20
}
]

Response code: 200; Time: 22ms; Content length: 246 bytes
---------------------------------------------------------
# 查询全部用户
GET http://localhost:8080/user

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 22 Nov 2018 08:14:11 GMT

[
{
"id": 1,
"name": "weidan",
"salary": 6000,
"age": 18
},
{
"id": 2,
"name": "xiaodan",
"salary": 10000,
"age": 36
},
{
"id": 3,
"name": "dadan",
"salary": 9000,
"age": 28
},
{
"id": 4,
"name": "Sally",
"salary": 12000,
"age": 24
},
{
"id": 5,
"name": "weisuodan",
"salary": 20000,
"age": 20
}
]

Response code: 200; Time: 29ms; Content length: 246 bytes
---------------------------------------------------------
# 新增用户接口 返回 201
POST http://localhost:8080/user?orgId=10

HTTP/1.1 201
Content-Length: 0
Date: Thu, 22 Nov 2018 08:16:23 GMT

<Response body is empty>

Response code: 201; Time: 232ms; Content length: 0 bytes
---------------------------------------------------------
# 更新用户接口 返回 204
PUT http://localhost:8080/user

HTTP/1.1 204
Date: Thu, 22 Nov 2018 08:16:56 GMT

<Response body is empty>

Response code: 204; Time: 79ms; Content length: 0 bytes
---------------------------------------------------------
# 删除用户接口
DELETE http://localhost:8080/user/2

HTTP/1.1 200
Content-Length: 0
Date: Thu, 22 Nov 2018 08:17:20 GMT

<Response body is empty>

Response code: 200; Time: 46ms; Content length: 0 bytes

三. 总结

JSON 已经成为各大语言的青睐,SpringMVC 还可以提供其他的数据格式交互,例如 Hession 。这些后面跟 SpringCloud 放在一起,跟 Feign 一起搭配使用,提升数据交互的效率。 其实做 RESTful 的时候,最纠结的就是资源的设计了,有的需要用这个条件获取,有的需要那个条件去获取,以前写的乱七八糟,自从知道了 params 以后,设计就变得简单易懂了。不仅仅 GetMapping 所有的请求方式都有这个参数。 以及还有 @SessionAttribute @CookieValue @RequestHeader 可以获取相对应区域的信息。交替使用则可以让开发更加流畅。