Fork me on GitHub

Springboot整合MongoDB

这节我们将整合Spring Boot与Mongo DB实现增删改查的功能,并且实现序列递增。Mongo DB下载地址:https://www.mongodb.com/download-center/community

新建一个Spring Boot项目,版本为2.1.3.RELEASE,并引入如下依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

然后可以通过Mongo Shell或者Mongo Compass工具创建一个名称为testdb的数据库,并新增user文档(文档,类似与关系型数据库里的数据表):

在配置文件application.yml里配置Mongo DB:

1
2
3
4
5
6
spring:
data:
mongodb:
host: localhost
port: 27017
database: testdb

Mongo DB的默认端口为27017,使用的数据库为刚刚创建的testdb。

创建User实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Document(collection = "user")
public class User {

@Id
private String id;

private String name;

private Integer age;

private String description;

// get set 略
}

@Document(collection = "user")表明这是一个文档对象,名称为user,对应Mongo DB里的user表。@Id标注主键字段,String类型的主键值在插入的时候Mongo DB会帮我们自动生成。如果对象中的某个属性为非表字段,可以使用注解@Transient进行排除。

准备好这些后,我们开始编写一些简单的增删改查样例。

简单增删改查

创建一个UserDao接口:

1
2
3
@Repository
public interface UserDao extends MongoRepository<User, String> {
}

接口继承自MongoRepository,泛型分别为实体对象和主键类型。通过继承MongoRepositoryUserDao包含了一些增删改查的方法,如下图所示:

接着编写UserService,为了方便这里不再编写接口:

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
@Service
public class UserService {

@Autowired
private UserDao userDao;

public List<User> getUsers() {
return userDao.findAll();
}

public Optional<User> getUser(String id) {
return this.userDao.findById(id);
}

/**
* 新增和修改都是 save方法,
* id 存在为修改,id 不存在为新增
*/
public User createUser(User user) {
user.setId(null);
return userDao.save(user);
}

public void deleteUser(String id) {
this.userDao.findById(id)
.ifPresent(user -> this.userDao.delete(user));
}

public void updateUser(String id, User user) {
this.userDao.findById(id)
.ifPresent(
u -> {
u.setName(user.getName());
u.setAge(user.getAge());
u.setDescription(user.getDescription());
this.userDao.save(u);
}
);
}
}

上面我们编写了基本的增删改查样例,新增和修改都是通过save方法完成的,当主键存在时则为修改,主键不存在则为新增。

最后编写一个RESTful的UserController(为了方便,没有对参数进行校验):

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
@RestController
@RequestMapping("user")
public class UserController {

@Autowired
private UserService userService;

@GetMapping
public List<User> getUsers() {
return userService.getUsers();
}

@PostMapping
public User createUser(User user) {
return userService.createUser(user);
}

@DeleteMapping("/{id}")
public void deleteUser(@PathVariable String id) {
userService.deleteUser(id);
}

@PutMapping("/{id}")
public void updateUser(@PathVariable String id, User user) {
userService.updateUser(id, user);
}

/**
* 根据用户 id查找
* 存在返回,不存在返回 null
*/
@GetMapping("/{id}")
public User getUser(@PathVariable String id) {
return userService.getUser(id).orElse(null);
}
}

启动项目,使用postman来测试接口的可用性。

测试新增用户:

新增成功,查看数据库:

测试查询用户:

查询成功。

测试通过用ID查找用户:

更新用户:

查看数据库是否更新成功:

更新成功。

最后测试通过用户ID删除用户:

返回状态码200,删除成功。

查看数据库,删除成功:

多条件查询

其实UserDao通过继承MongoRepository已经具有了JPA的特性,我们可以通过方法名来构建多查询条件的SQL。比如通过用户的年龄段来查询:

1
2
3
4
5
6
7
8
9
10
11
12
@Repository
public interface UserDao extends MongoRepository<User, String> {

/**
* 根据年龄段来查找
*
* @param from from
* @param to to
* @return List<User>
*/
List<User> findByAgeBetween(Integer from, Integer to);
}

在输入findBy后,IDEA会根据实体对象的属性和SQL的各种关键字自动组合提示:

比如再在创建一个通过年龄段,用户名和描述(模糊查询)查询用户的方法:

1
2
3
4
5
6
7
8
9
10
/**
* 通过年龄段,用户名,描述(模糊查询)
*
* @param from from
* @param to to
* @param name name
* @param description description
* @return List<User>
*/
List<User> findByAgeBetweenAndNameEqualsAndDescriptionIsLike(Integer from, Integer to, String name, String description);

方法参数个数需要和方法名中所需要的参数个数对应上。

排序与分页

排序和分页需要使用MongoTemplate对象来完成,在UserService里新增一个getUserByCondition方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Autowired
private MongoTemplate template;

public Page<User> getUserByCondition(int size, int page, User user) {
Query query = new Query();
Criteria criteria = new Criteria();

if (!StringUtils.isEmpty(user.getName())) {
criteria.and("name").is(user.getName());
}
if (!StringUtils.isEmpty(user.getDescription())) {
criteria.and("description").regex(user.getDescription());
}

query.addCriteria(criteria);

Sort sort = new Sort(Sort.Direction.DESC, "age");
Pageable pageable = PageRequest.of(page, size, sort);

List<User> users = template.find(query.with(pageable), User.class);
return PageableExecutionUtils.getPage(users, pageable, () -> template.count(query, User.class));
}

size表示每页显示的条数,page表示当前页码数,0表示第一页。上面的方法通过namedescription(模糊查询)来查询用户分页信息,并且查询结果使用age字段降序排序。方法返回Page对象。

UserController里添加:

1
2
3
4
@GetMapping("/condition")
public Page<User> getUserByCondition(int size, int page, User user) {
return userService.getUserByCondition(size, page, user);
}

重启项目,我们往数据库里多加几条数据:

获取第1页数据,每页显示10条:

返回数据:

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
{
"content": [
{
"id": "5ca56ae2f08f0b6048fd470d",
"name": "jane",
"age": 26,
"description": "web developer"
},
{
"id": "5ca56ad1f08f0b6048fd470c",
"name": "scott",
"age": 23,
"description": "ui designer"
},
{
"id": "5ca56afaf08f0b6048fd470e",
"name": "mike",
"age": 21,
"description": "python developer"
},
{
"id": "5ca56b38f08f0b6048fd470f",
"name": "mrbird",
"age": 18,
"description": "java noob"
}
],
"pageable": {
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"pageSize": 10,
"pageNumber": 0,
"unpaged": false,
"paged": true
},
"last": true,
"totalPages": 1,
"totalElements": 4,
"number": 0,
"size": 10,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"numberOfElements": 4,
"first": true,
"empty": false
}