【MongoDB】05 Spring-Data-MongDB
关于MDBC的使用,据说操作太难了,干脆跳过不学,地址:
https://www.runoob.com/mongodb/mongodb-java.html
由Spring整合封装好的一套操作拿来用就行了:
IDEA创建SpringBoot项目:
只勾选主要的辅助工具即可

引入Maven坐标:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>
配置YML信息:
spring: data: mongodb: host: 127.0.0.1 port: 27017 database: bbs
项目运行测试正常:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.3.RELEASE) 2020-09-11 11:56:50.237 INFO 12976 --- [ restartedMain] cn.dzz.MdbcApplication : Starting MdbcApplication on DESKTOP-MCKUUKV with PID 12976 (C:\Users\User-Dai\IdeaProjects\Spring-Data-MongoDB\target\classes started by User-Dai in C:\Users\User-Dai\IdeaProjects\Spring-Data-MongoDB) 2020-09-11 11:56:50.241 INFO 12976 --- [ restartedMain] cn.dzz.MdbcApplication : No active profile set, falling back to default profiles: default 2020-09-11 11:56:50.315 INFO 12976 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable 2020-09-11 11:56:50.880 INFO 12976 --- [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data MongoDB repositories in DEFAULT mode. 2020-09-11 11:56:50.911 INFO 12976 --- [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 24ms. Found 0 MongoDB repository interfaces. 2020-09-11 11:56:51.293 INFO 12976 --- [ restartedMain] org.mongodb.driver.cluster : Cluster created with settings {hosts=[127.0.0.1:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms'} 2020-09-11 11:56:51.404 INFO 12976 --- [127.0.0.1:27017] org.mongodb.driver.connection : Opened connection [connectionId{localValue:1, serverValue:4}] to 127.0.0.1:27017 2020-09-11 11:56:51.411 INFO 12976 --- [127.0.0.1:27017] org.mongodb.driver.cluster : Monitor thread successfully connected to server with description ServerDescription{address=127.0.0.1:27017, type=STANDALONE, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=9, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=4931200} 2020-09-11 11:56:51.745 INFO 12976 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729 2020-09-11 11:56:51.776 INFO 12976 --- [ restartedMain] cn.dzz.MdbcApplication : Started MdbcApplication in 2.314 seconds (JVM running for 4.078) Process finished with exit code 0
实体类:
@Data @AllArgsConstructor @NoArgsConstructor @Document("user") // 指定MongoDB中的文档 @CompoundIndex(def = "{'id':1, 'age': -1}") // 使用 @CompoundIndex(def = "{'id':1, 'age': -1}") 表示复合索引 public class User implements Serializable { private static final long serialVersionUID = -8297090478392371913L; @Id // 主键,该属性会自动对应_id字段。如果该属性名称就叫id,那么注解可以省略 private String id; @Field("name") // 指定该属性对应集合中的name列,如果属性名已经和name对应了就可以不写 private String name; private Integer age; private String address; @Indexed // 使用 @Indexed 指定单列索引 private Integer sex; private Date createdTime; private Integer state; private Integer followNum; }
接口:
继承MongoRepository,指定实体和主键的类型作为泛型
public interface UserRepository extends MongoRepository<User, String> { }
该接口的源码:
@NoRepositoryBean public interface MongoRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { <S extends T> List<S> saveAll(Iterable<S> var1); List<T> findAll(); List<T> findAll(Sort var1); <S extends T> S insert(S var1); <S extends T> List<S> insert(Iterable<S> var1); <S extends T> List<S> findAll(Example<S> var1); <S extends T> List<S> findAll(Example<S> var1, Sort var2); }
业务类:
@Service public class UserService { @Autowired private UserRepository userRepository; public void createUser(User user) { userRepository.save(user); } public void updateUser(User user) { userRepository.save(user); } public void deleteUserById(String id) { userRepository.deleteById(id); } public List<User> getAllUserList() { return userRepository.findAll(); } public User getUserById(String id) { return userRepository.findById(id).get(); } }
插入数据测试:
@SpringBootTest public class MongoDbTest { @Autowired private UserService userService; @Test public void createTest() { User user = new User( null, "杰哥", 23, "中国-台湾-台北市-...。。。", 1, new Date(), 1, 0 ); userService.createUser(user); } }
测试类:
package cn.dzz; import cn.dzz.pojo.User; import cn.dzz.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Date; import java.util.List; import java.util.Random; /** * @author Echo42 * @file Spring-Data-MongoDB * @create 2020年09月11日12:12 * @description */ @SpringBootTest public class MongoDbTest { @Autowired private UserService userService; /** * 创建 * 这个创建对象并不能循环执行,不知道为什么 */ @Test public void createTest() { User user = new User( null, "杰哥", 23, "中国-台湾-台北市-...。。。", 1, new Date(), 1, 0 ); userService.createUser(user); } /** * 查询所有 */ @Test public void findAll() { List<User> userList = userService.getAllUserList(); for (User user : userList) { System.out.println(user); } } /** * 按ID查询 */ @Test public void findById() { User userById = userService.getUserById("5f5afb3ec4ce1f52da0fc99b"); System.out.println(userById); } /** * 更新 */ @Test public void updateById() { User userById = userService.getUserById("5f5afb3ec4ce1f52da0fc99b"); userById.setName("哈哈哈哈哈哈哈哈哈"); userService.updateUser(userById); } /** * 删除 */ @Test public void deleteById() { userService.deleteUserById("5f5afb3ec4ce1f52da0fc99b"); } }
JPA查询案例:
根据启用状态和性别查询
    /**
     * 根据启用状态和性别查询
     * @param state
     * @param sex
     * @return
     */
    List<User> getUserByStateAndSex(Integer state, Integer sex);
业务类:
public List<User> getUserByStateAndSex(Integer state, Integer sex) {
    return userRepository.getUserByStateAndSex(state, sex);
}
测试类:
    /**
     * 删除
     */
    @Test
    public void getByStateAndSex() {
        List<User> userByStateAndSex = userService.getUserByStateAndSex(1, 1);
        for (User byStateAndSex : userByStateAndSex) {
            System.out.println(byStateAndSex);
        }
    }
打印结果:
2020-09-11 13:02:16.692  INFO 3264 --- [           main] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:23}] to 127.0.0.1:27017
User(id=5f5afa2589bbbe4373b76f04, name=杰哥, age=23, address=中国-台湾-台北市-...。。。, sex=1, createdTime=Fri Sep 11 12:16:37 CST 2020, state=1, followNum=0)
2020-09-11 13:02:16.808  INFO 3264 --- [extShutdownHook] org.mongodb.driver.connection            : Closed connection [connectionId{localValue:2, serverValue:23}] to 127.0.0.1:27017 because the pool has been closed.
Process finished with exit code 0
MongoTemplate使用:
现在有个需求,我们需要给某个用户的关注量+1,下面的代码是实现方案
按照JPA规范的业务实现:
public void incrementFollowerCount(String id){ Optional<User> optionalUser = userRepository.findById(id); User user = optionalUser.get(); user.setFollowNum(user.getFollowNum() + 1); userRepository.save(user); }
功能实现没有问题,但是问题是:
我们只需要给关注量+1,并不需要姓名、地址等这些数据,因此也就没必要查询出这些字段,甚至于根本就不需要查询操作,直接更新就可以了。
使用MT:
首先配置MT
@Autowired private MongoTemplate mongoTemplate;
然后编写方法:
public void incrementFollowerCountByMT(String id){ Criteria criteria = Criteria.where("_id"); // 要进行查询的字段 注入 criteria.is(id); // 和参数进行比较 Query query = Query.query(criteria); // 封装条件 Update update = new Update(); update.inc("followNum"); // 提供好的自增 mongoTemplate.updateFirst(query, update, User.class); }
测试:
@Test public void getBy() { userService.incrementFollowerCountByMT("5f5afa2589bbbe4373b76f04"); }
分页查询:
dao层:
/** * * @param age * @param pageable * @return */ Page<User> findByAge(Integer age, Pageable pageable);
业务类:
/** * 根据名称分页查询 * * @param age * @param page * @param size * @return */ public Page<User> findByAgePage(Integer age, int page, int size) { // 构造分页对象 PageRequest pageRequest = PageRequest.of(page - 1, size); return userRepository.findByAge(age, pageRequest); }
测试类:
@Test public void testFindByAgePage() { Page<User> users = userService.findByAgePage(18, 1, 2); System.out.println("总条数:" + users.getTotalElements()); System.out.println("总页数:" + users.getTotalPages()); System.out.println("本页数据:"); List<User> userList = users.getContent(); for (User user : userList) { System.out.println(user); } }
打印结果:
2020-09-11 13:31:47.891  INFO 4356 --- [           main] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:31}] to 127.0.0.1:27017
总条数:11
总页数:6
本页数据:
User(id=5f5b0b526903ab1240fef72d, name=用户 13, age=18, address=中国-台湾-台北市, sex=0, createdTime=Fri Sep 11 13:29:54 CST 2020, state=0, followNum=0)
User(id=5f5b0b526903ab1240fef736, name=用户 22, age=18, address=中国-台湾-台北市, sex=0, createdTime=Fri Sep 11 13:29:54 CST 2020, state=0, followNum=0)
2020-09-11 13:31:48.113  INFO 4356 --- [extShutdownHook] org.mongodb.driver.connection            : Closed connection [connectionId{localValue:2, serverValue:31}] to 127.0.0.1:27017 because the pool has been closed.
Process finished with exit code 0
结合MongoTemplate进行分页查询:
/** * 使用MongoTemplate分页查询 * * @param page * @param size * @param user * @return */ public List<User> findPageByTemplate(int page, int size, User user) { // 构造一个查询对象 Query query = new Query(); // 设置参数 if (!StringUtils.isEmpty(user.getName())) { query.addCriteria(Criteria.where("name").regex(user.getName() + ".*")); } if (user.getAge() != null) { query.addCriteria(Criteria.where("age").lt(user.getAge())); } if (user.getSex() != null) { query.addCriteria(Criteria.where("sex").is(user.getSex())); } // 跳过多少条 query.skip((page - 1) * size); // 取出多少条 query.limit(size); // 构造排序对象 Sort.Order order = new Sort.Order(Sort.Direction.DESC, "age"); // 设置排序对象 query.with(Sort.by(order)); return mongoTemplate.find(query, User.class); }
测试类:
@Test public void testFindByTemplatePage() { // user在这里作为一个封装好的查询条件赋予对象, // 在项目中我们会使用子类UserViewObject二次封装。 // 注意实体类不可以为NULL,可以无属性 List<User> pageByTemplate = userService.findPageByTemplate(3, 6, new User()); for (User user : pageByTemplate) { System.out.println(user); } }
结果
User(id=5f5b0b80a1c41c3324467f8d, name=用户 62, age=23, address=中国-台湾-台北市, sex=-1, createdTime=Fri Sep 11 13:30:40 CST 2020, state=0, followNum=0) User(id=5f5b0b526903ab1240fef74d, name=用户 45, age=22, address=中国-台湾-台北市, sex=0, createdTime=Fri Sep 11 13:29:54 CST 2020, state=0, followNum=0) User(id=5f5b0b80a1c41c3324467f68, name=用户 25, age=22, address=中国-台湾-台北市, sex=0, createdTime=Fri Sep 11 13:30:40 CST 2020, state=0, followNum=0) User(id=5f5b0b526903ab1240fef761, name=用户 65, age=22, address=中国-台湾-台北市, sex=0, createdTime=Fri Sep 11 13:29:54 CST 2020, state=0, followNum=0) User(id=5f5b0b526903ab1240fef753, name=用户 51, age=22, address=中国-台湾-台北市, sex=0, createdTime=Fri Sep 11 13:29:54 CST 2020, state=0, followNum=0) User(id=5f5afb7eb3fa6d47d8215b7a, name=用户98, age=22, address=中国-台湾-台北市-...。。。, sex=0, createdTime=Fri Sep 11 12:22:22 CST 2020, state=1, followNum=0) 2020-09-11 13:38:27.734 INFO 12772 --- [extShutdownHook] org.mongodb.driver.connection : Closed connection [connectionId{localValue:2, serverValue:34}] to 127.0.0.1:27017 because the pool has been closed. Process finished with exit code 0
@Query注解
像Mybatis一样,虽然MongoDB提供绝大部分的条件处理,但是总是会有特别复杂的条件需要查询,
还是会出现需要手写SQL查询的状况,这时候Spring-Data-MongoDB提供了@Query注解,支持我们在Dao接口的方法上注解
很像Mybatis的@Select @Update @Delete @Insert
需求1:根据 state 和 age 查询
这里我们需要使用 ?数字占位符 表达式来取出参数中指定位置的值,占位符从0开始。
@Query("{ state: ?0, age: ?1  }")
List<User> selectEnableUserByAge(Integer state, Integer age);
需求2:有时候我们的参数可能过多,条件也可能不同,我们想传入一个实体类进行查询,直接取出实体类中的属性进行条件构造。
这里我们需要使用 SpEL 表达式,格式:?#{} 括号中使用 [下标] 来取出指定位置的参数,如 ?#{[0]} 则取出第一个参数。之后直接取出参数中的指定属性即可,如 ?#{[1].age} 就是取出第二个参数的age属性。
现在我们需要查询 性别为女,或者年龄在18岁以下的所有启用中的用户
@Query("{  state: ?#{[0].state}, $or: [ {sex: ?#{[0].sex}}, {age: { $lt: ?#{[0].age} }}  ] }")
List<User> selectByEntity(User user);
同时,如果我们只想获取指定的字段,我们还可以使用第二个参数 fields 来进行投影查询
   @Query(value = "{  state: ?#{[0].state}, $or: [ {sex: ?#{[0].sex}}, {age: { $lt: ?#{[0].age} }}  ] }",
            fields = "{ name:1, age:1 }")
    List<User> selectByEntity(User user);
SpringDataMongoDB连接认证
前面我们学到了安全认证。
当数据库配置了安全认证后,想要使用SpringDataMongoDB连接MongoDB,
就需要使用 username:password@hostname/dbname 格式 ,username为用户名,password为密码
spring: data: mongodb: uri: mongodb://jigege:123456@39.102.38.0:27017/bbs?authSource=admin&authMechanism=SCRAM-SHA-1
JPA查询
根据启用状态和性别查询
    /**
     * 根据启用状态和性别查询
     * @param state
     * @param sex
     * @return
     */
    List<User> getUserByStateAndSex(Integer state, Integer sex);
业务类:
public List<User> getUserByStateAndSex(Integer state, Integer sex) {
    return userRepository.getUserByStateAndSex(state, sex);
}
测试类:
    /**
     * 删除
     */
    @Test
    public void getByStateAndSex() {
        List<User> userByStateAndSex = userService.getUserByStateAndSex(1, 1);
        for (User byStateAndSex : userByStateAndSex) {
            System.out.println(byStateAndSex);
        }
    }
打印结果:
2020-09-11 13:02:16.692  INFO 3264 --- [           main] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:23}] to 127.0.0.1:27017
User(id=5f5afa2589bbbe4373b76f04, name=杰哥, age=23, address=中国-台湾-台北市-...。。。, sex=1, createdTime=Fri Sep 11 12:16:37 CST 2020, state=1, followNum=0)
2020-09-11 13:02:16.808  INFO 3264 --- [extShutdownHook] org.mongodb.driver.connection            : Closed connection [connectionId{localValue:2, serverValue:23}] to 127.0.0.1:27017 because the pool has been closed.
Process finished with exit code 0
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号