springboot集成MongoDb引入依赖
org.springframework.boot spring-boot-starter-data-mongodb
看一下MongoRepository接口,它集成了 spring-data-commons的PagingAndSortingRepository接口和QueryByExampleExecutor接口
spring-data包介绍public interface MongoRepositoryextends PagingAndSortingRepository , QueryByExampleExecutor
Spring Data 是spring的组件之一,主要目的是为了让开发者再工作中能更加轻松的完成CURD,简化代码应该是所有框架的目的吧。Spring-data-commons只是其中的一个模块而已,spring-data-mongo包含了spring-data-comons,另外spring-data-JPA也是实现了spring-data-common的接口完成简单的CRUD操作以及复杂自定义的查询更新操作。
JPA操作手册:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
最底层的接口是:
package org.springframework.data.repository;@Indexed public interface Repository{ } 中央存储库标记接口,通过实现这个接口进行扩展查询比如分页,排序等操作
jpa自定义查询的一些规范
查询创建
通常,JPA 的查询创建机制如“查询方法”中所述。以下示例显示了 JPA 查询方法转换为的内容:
示例 57. 从方法名称创建查询公共接口 UserRepository 扩展 Repository{ List findByEmailAddressAndLastname(String emailAddress, String lastname); }
| 关键词 | 样本 | JPQL 片段 |
|---|---|---|
| Distinct | findDistinctByLastnameAndFirstname | select distinct … where x.lastname = ?1 and x.firstname = ?2 |
| And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
| Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
| Is,Equals | findByFirstname, findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
| Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
| LessThan | findByAgeLessThan | … where x.age < ?1 |
| LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
| GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
| GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
| After | findByStartDateAfter | … where x.startDate > ?1 |
| Before | findByStartDateBefore | … where x.startDate < ?1 |
| IsNull,Null | findByAge(Is)Null | … where x.age is null |
| IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
| Like | findByFirstnameLike | … where x.firstname like ?1 |
| NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
| StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1(与 append 绑定的参数%) |
| EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1(以 prepended 绑定的参数%) |
| Containing | findByFirstnameContaining | … where x.firstname like ?1(参数绑定在 中%) |
| OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
| Not | findByLastnameNot | … where x.lastname <> ?1 |
| In | findByAgeIn(Collection ages) | … where x.age in ?1 |
| NotIn | findByAgeNotIn(Collection ages) | … where x.age not in ?1 |
| True | findByActiveTrue() | … where x.active = true |
| False | findByActiveFalse() | … where x.active = false |
| IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
自定义实体类:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
@Id
private String id;
private String userName;
private String userId;
private Integer age;
private String sex;
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
private Date birthDay;
}
定义Dao层操作MongoDB
@Repository public interface UserRepository extends MongoRepository{ List findByUserNameLike(String userName); }
service层
public interface UserService {
List findUserName(String userName);
void saveUser(User user);
void deleteUser(String userId);
Page findUserPage(Pageable pageable);
void updateUserInfo(User user);
List findLikeUserName(String userName);
List findAll();
}
controller层:
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/find/userName")
public CommonResponse findUserName(@RequestParam("userName") String userName) {
List users = userService.findUserName(userName);
return CommonResponse.success(users);
}
@GetMapping("/find/like")
public CommonResponse findLikeUserName(@RequestParam("userName") String userName) {
List users = userService.findLikeUserName(userName);
return CommonResponse.success(users);
}
@PostMapping("/save")
public CommonResponse save(@RequestBody User user) {
userService.saveUser(user);
return CommonResponse.success();
}
@PutMapping("/update")
public CommonResponse update(@RequestBody User user) {
userService.updateUserInfo(user);
return CommonResponse.success();
}
@GetMapping("/page")
public CommonResponse page(@RequestParam Integer page, @RequestParam Integer limit) {
MongoPageAble mongoPageAble = MongoPageAble.of(page, limit);
Page page1 = userService.findUserPage(mongoPageAble);
return CommonResponse.success(new PageInfoDTO<>(page1));
}
@DeleteMapping("/delete")
public CommonResponse delete(@RequestParam String userId) {
userService.deleteUser(userId);
return CommonResponse.success();
}
@GetMapping("/find/all")
public CommonResponse findAll() {
List users = userService.findAll();
return CommonResponse.success(users);
}
}
提供自定义的分页查询数据PO:
public class MongoPageAble extends AbstractPageRequest {
private Sort sort;
public MongoPageAble(int pageNum, int pageSize, Sort sort) {
super(pageNum, pageSize);
this.sort = sort;
}
public static MongoPageAble of(int page, int size) {
return of(page, size, Sort.unsorted());
}
public static MongoPageAble of(int page, int size, Sort sort) {
return new MongoPageAble(page, size, sort);
}
public static MongoPageAble of(int page, int size, Sort.Direction direction, String... properties) {
return of(page, size, Sort.by(direction, properties));
}
@Override
public Sort getSort() {
return sort;
}
@Override
public Pageable next() {
return new MongoPageAble(this.getPageNumber() + 1, this.getPageSize(), this.getSort());
}
@Override
public Pageable previousOrFirst() {
return this.hasPrevious() ? this.previous() : this.first();
}
@Override
public Pageable previous() {
return this.getPageNumber() == 1 ? this : new MongoPageAble(this.getPageNumber() - 1, this.getPageSize(), this.getSort());
}
@Override
public Pageable first() {
return new MongoPageAble(1, this.getPageSize(), this.getSort());
}
public void setSort(Sort sort) {
this.sort = sort;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof MongoPageAble)) {
return false;
} else {
MongoPageAble that = (MongoPageAble)obj;
return super.equals(that) && this.sort.equals(that.sort);
}
}
@Override
public int hashCode() {
return 31 * super.hashCode() + this.sort.hashCode();
}
@Override
public String toString() {
return String.format("Page request [number: %d, size %d, sort: %s]", this.getPageNumber(), this.getPageSize(), this.sort);
}
@Override
public long getOffset() {
return ( (getPageNumber()-1) * getPageSize());
}
}
其中需要注意的是getOffset方法,因为mongoDB在实际执行分页查询时候,走的是skip().limit()操作,skip表示跳过查询的数量,这里传入的参数offset偏移量,而我们接口实际查询的时候页数page是从1开始的,mongo在计算偏移量的时候是offset=page*size 导致跳过第一页数据
自定义封装分页查询结果:
@Data @NoArgsConstructor public class PageInfoDTO{ private Integer totalCount; private Integer pageSize; private Integer totalPage; private Integer startPage; private List list; public PageInfoDTO(List list, Integer pageSize, Integer startPage) { this.list = list; this.totalCount = list.size(); this.pageSize = pageSize; this.startPage = startPage; this.totalPage = (int) Math.ceil((double) totalCount / pageSize); } public PageInfoDTO(IPage page) { this.list = page.getRecords(); this.totalCount = (int) page.getTotal(); this.pageSize = (int) page.getSize(); this.startPage = (int) page.getCurrent(); this.totalPage = (int) page.getPages(); } public PageInfoDTO(Page page) { // this.list = page.get().collect(Collectors.toList()); this.list = page.getContent(); this.totalCount = (int) page.getTotalElements(); this.pageSize = page.getSize(); this.startPage = page.getPageable().getPageNumber(); this.totalPage = page.getTotalPages(); } }
下面开始实际测试:
插入数据:
查询全部数据:
http://localhost:8080/user/find/all
查询结果:
{
"success": true,
"code": "200",
"msg": "操作成功",
"data": [
{
"id": "1111",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "男",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "222",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "男",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "333",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "男",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "444",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "555",
"userName": "小王22",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "666",
"userName": "小王11",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
}
]
}
模糊查询:
http://localhost:8080/user/find/like?userName=小王
查询结果:
{
"success": true,
"code": "200",
"msg": "操作成功",
"data": [
{
"id": "1111",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "男",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "222",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "男",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "333",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "男",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "444",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "555",
"userName": "小王22",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "666",
"userName": "小王11",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
}
]
}
分页查询:
localhost:8080/user/page?page=2&limit=3
查询结果:
{
"success": true,
"code": "200",
"msg": "操作成功",
"data": {
"totalCount": 6,
"pageSize": 3,
"totalPage": 2,
"startPage": 2,
"list": [
{
"id": "444",
"userName": "小王",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "555",
"userName": "小王22",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
},
{
"id": "666",
"userName": "小王11",
"userId": "23",
"age": 23,
"sex": "女",
"birthDay": "2020-11-20 20:02:20"
}
]
}
}
在实际开发中也可使用MongoTemplate代替上面的操作 ,后续会介绍MongoTemplate的CRUD操作以及分页聚合排序等



