【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

 

posted @ 2020-09-11 12:29  emdzz  阅读(889)  评论(0)    收藏  举报