第 4-7 課:Spring Boot 簡單集成 MongoDB

MongoDB 如今是最流行的 NoSQL 數據庫,被廣泛應用於各行各業中,很多創業公司數據庫選型就直接使用了 MongoDB,但對於大部分公司,使用 MongoDB 的場景是做大規模數據查詢和離線分析。MongoDB 一經推出就受到了廣大社區的熱愛,可以說是對程序員最友好的一種數據庫,下面我們來了解一下它的特性。

MongoDB 簡介

MongoDB(Humongous,龐大)是可以應用於各種規模的企業、各個行業以及各類應用程序的開源數據庫,作爲一個適用於敏捷開發的數據庫,MongoDB 的數據模式可以隨着應用程序的發展而靈活地更新。與此同時,它也爲開發人員提供了傳統數據庫的功能:二級索引、完整的查詢系統及嚴格一致性等。MongoDB 能夠使企業更加具有敏捷性和可擴展性,各種規模的企業都可以通過使用 MongoDB 來創建新的應用,來提高與客戶之間的工作效率,加快產品上市時間,以及降低企業成本。

MongoDB 是專門爲可擴展性、高性能和高可用性而設計的數據庫,它可以從單服務器部署擴展到大型、複雜的多數據中心架構。利用內存計算的優勢,MongoDB 能夠提供高性能的數據讀寫操作。 MongoDB 的本地複製和自動故障轉移功能使應用程序具有企業級的可靠性和操作靈活性。

MongoDB 相關概念

在學習 MongoDB 之前需要先了解一些專業術語,常說 MongoDB 是最像關係數據庫的 NoSQL 數據庫,我們用關係數據庫和 MongoDB 做一下對比,方便更清晰地認識它。

SQL 術語/概念 MongoDB 術語/概念 解釋/說明
DataBase DataBase 數據庫
Table Collection 數據庫表/集合
Row Document 數據記錄行/文檔
Column Field 數據字段/域
index index 索引
Table joins   表連接,MongoDB 不支持
primary key primary key 主鍵,MongoDB 自動將 _id 字段設置爲主鍵

MongoDB 和關係數據庫一樣有庫的概念,一個 MongoDB 數據庫可以有很多數據庫,MongoDB 中的集合就相當於我們關係數據庫中的表,文檔就相當於關係數據庫中的行,域就相當於關係數據庫中的列,MongoDB 也支持各種索引有唯一主鍵,但不支持表連接查詢。

Spring Boot MongoDB

Spring Boot 操作數據庫的各種 Starters 都繼承於 Spring Data,Spring Data 是 Spring 爲了簡化數據庫操作而封裝的一個組件包,提供了操作多種數據庫的支持,其 API 簡潔、調用方便。

spring-boot-starter-data-mongodb 是 Spring Data 的一個子模塊。目標是爲 MongoDB 提供一個相近的、一致的、基於 Spring 的編程模型。spring-boot-starter-data-mongodb 核心功能是映射 POJO 到 Mongo 的 DBCollection 中的文檔,並且提供 Repository 風格數據訪問層。

我們使用 Spring Boot 進行 MongoDB 連接,需要使用 spring-boot-starter-data-mongodb 包。spring-boot-starter-data-mongodb 繼承 Spring Data 的通用功能外,針對 MongoDB 的特性開發了很多定製的功能,讓我們使用 Spring Boot 操作 MongoDB 更加簡便。

Spring Boot 操作 MongoDB 有兩種比較流行的使用方法,一種是將 MongoTemplate 直接注入到 Dao 中使用,一種是繼承 MongoRepository,MongoRepository 內置了很多方法可直接使用。

我們分別來介紹它們的使用。

MongoTemplate 的使用

MongoTemplate 提供了非常多的操作 MongoDB 方法,它是線程安全的,可以在多線程的情況下使用。

MongoTemplate 實現了 MongoOperations 接口, 此接口定義了衆多的操作方法如 find、findAndModify、findOne、insert、remove、save、update and updateMulti 等。它轉換 domain object 爲 DBObject,並提供了 Query、Criteria and Update 等流式 API。

我們後面在使用動態查詢時候會用到 DBObject 對象的操作。

(1)pom 包配置

pom 包裏面添加 spring-boot-starter-data-mongodb 包引用:

<dependencies>
    <dependency> 
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency> 
</dependencies>

(2)在 application.properties 中添加配置

spring.data.mongodb.uri=mongodb://name:pass@localhost:27017/test

多個 IP 集羣可以採用以下配置:

spring.data.mongodb.uri=mongodb://user:pwd@ip1:port1,ip2:port2/database

當然了密碼和用戶名如果沒有設置可以不用添加,像這樣:

spring.data.mongodb.uri=mongodb://localhost:27017/test

注:Mongo 3.0 Java 驅動不支持 spring.data.mongodb.host 和 spring.data.mongodb.port,對於這種情況,spring.data.mongodb.uri 需要提供全部的配置信息。

(3)創建數據實體

public class User implements Serializable {
        private static final long serialVersionUID = -3258839839160856613L;
        private Long id;
        private String userName;
        private String passWord;

      //getter、setter 省略
}

(4)對實體進行增刪改查操作

Repository 層實現了 User 對象的增、刪、改、查功能。

首先將 MongoTemplate 注入到實體類中:

@Component
public class UserRepositoryImpl implements UserRepository {

    @Autowired
    private MongoTemplate mongoTemplate;

}

做增、刪、改、查功能時,直接使用即可。

添加數據:

@Override
public void saveUser(UserEntity user) {
    mongoTemplate.save(user);
}

save 方法中會進行判斷,如果對象包含了 ID 信息就會進行更新,如果沒有包含 ID 信息就自動保存。

根據用戶名查詢對象:

@Override
public User findUserByUserName(String userName) {
    Query query=new Query(Criteria.where("userName").is(userName));
    User user =  mongoTemplate.findOne(query , User.class);
    return user;
}

更新對象比較靈活,可以選擇更新一條數據,或者更新多條數據。

@Override
public long updateUser(User user) {
    Query query=new Query(Criteria.where("id").is(user.getId()));
    Update update= new Update().set("userName", user.getUserName()).set("passWord", user.getPassWord());
    //更新查詢返回結果集的第一條
    UpdateResult result =mongoTemplate.updateFirst(query,update,User.class);
    //更新查詢返回結果集的所有
    // mongoTemplate.updateMulti(query,update,UserEntity.class);
    if(result!=null)
        return result.getMatchedCount();
    else
        return 0;
}

刪除對象:

@Override
public void deleteUserById(Long id) {
    Query query=new Query(Criteria.where("id").is(id));
    mongoTemplate.remove(query,User.class);
}

(5)開發對應的測試方法

測試時注入封裝好的 UserRepository,根據情況調用對應的方法即可:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTest {
    @Autowired
    private UserRepository userRepository;
}

測試插入數據:

@Test
public void testSaveUser() throws Exception {
    User user=new User();
    user.setId(2l);
    user.setUserName("小明");
    user.setPassWord("fffooo123");
    userRepository.saveUser(user);
}

測試查詢方法:

@Test
public void findUserByUserName(){
    User user= userRepository.findUserByUserName("小明");
   System.out.println("user is "+user);
}

測試更新:

@Test
public void updateUser(){
    User user=new User();
    user.setId(2l);
    user.setUserName("天空");
    user.setPassWord("fffxxxx");
    userRepository.updateUser(user);
}

最後測試刪除:

@Test
public void deleteUserById(){
    userRepository.deleteUserById(1L);
}

(6)查看驗證結果

可以使用工具 Robo 3T 來連接後直接圖形化展示查看,也可以登錄服務器用命令來查看。

a.登錄 mongos:

bin/mongo -host localhost -port 20000

b.切換到 test 庫:

use test

c.查詢 userEntity 集合數據:

db.user.find()

根據 c 查詢的結果來觀察測試用例的執行是否正確,到此 MongoTemplate 的增、刪、改、查功能已經全部實現。

MongoRepository 的使用

MongoRepository 繼承於 PagingAndSortingRepository,再往上就是 CrudRepository,根據下面的類圖可以看出 MongoRepository 和前面 JPA、Elasticsearch 的使用比較類似,都是 Spring Data 家族的產品,最終使用方法也就和 JPA、ElasticSearch 的使用方式類似,可以根據方法名自動生成 SQL 來查詢。

MongoRepository 項目結構和上面類似,只有 Repository 層的代碼有所變化,如下:

public interface UserRepository extends MongoRepository<UserEntity, Long> {
    UserEntity findByUserName(String userName);
}

我們新建 UserRepository 需要繼承 MongoRepository,這樣就可直接使用 MongoRepository 的內置方法。

使用 UserRepository 進行增、刪、改、查功能,首先將 UserRepository 注入到類中:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserDaoTest {
    @Autowired
    private UserRepository userRepository;
}

集成 MongoRepository 會默認實現很多的內置方法,其中就包括保持、刪除等操作。

測試保存數據:

@Test
public void testSaveUser() throws Exception {
    User user=new User();
    user.setId(i);
    user.setUserName("小明"+i);
    user.setPassWord("fffooo123");
    userRepository.save(user);
}

測試根據屬性查詢:

@Test
public void findUserByUserName(){
  User user= userRepository.findByUserName("小明");
   System.out.println("user is "+user);
}

測試更新屬性,保存和更新都使用的是 save() 方法,內部會根據實體是否有註解來判斷是用來更新還是用來保存。

@Test
public void updateUser(){
    User user=new User();
    user.setId(2l);
    user.setUserName("天空");
    user.setPassWord("fffxxxx");
    userRepository.save(user);
}

測試刪除數據:

@Test
public void deleteUserById(){
    userRepository.delete(1l);
}

我們發現 UserRepository 已經自動實現了常用的數據庫操作,不用像使用 MongoTemplate 那樣需要手動來實現。

如果想使用分頁怎麼辦?只需要在 UserRepository 再加入一個空方法,不需要自己實現。

Page<UserEntity> findAll(Pageable var1);

首先使用 save 方法插入 100 個 UserEntity:

@Test
public void testSaveUser() throws Exception {
    for (long i=0;i<100;i++) {
        User user=new User();
        user.setId(i);
        user.setUserName("小明"+i);
        user.setPassWord("fffooo123");
        userRepository.save(user);
    }
}

返回測試分頁,設置以 ID 的倒敘排序,每頁 10 條數據,取第二頁的用戶列表。

@Test
public void testPage(){
    Sort sort = new Sort(Sort.Direction.DESC, "id");
    Pageable pageable = PageRequest.of(2, 10, sort);
    Page page=userRepository.findAll(pageable);
    System.out.println("users: "+page.getContent().toString());
}
  • Pageable 是 Spring Data 庫中定義的一個接口,該接口是所有分頁相關信息的一個抽象,通過該接口,可以得到和分頁相關所有信息(如 pageNumber、pageSize 等),這樣,JPA 就能夠通過 pageable 參數來得到一個帶分頁信息的 SQL 語句。
  • Page 類也是 Spring Data 提供的一個接口,該接口表示一部分數據的集合以及其相關的下一部分數據、數據總數等相關信息,通過該接口,可以得到數據的總體信息(數據總數、總頁數...)以及當前數據的信息(當前數據的集合、當前頁數等)。

在 Spring Boot 2.0 中 new PageRequest(2, 10, sort) 已經過期不再推薦使用,2.0 中推薦使用 PageRequest.of(2, 10, sort) 來做排序參數控制。

我們發現使用 Repository 的方式和前幾課介紹的 Spring Boot JPA 非常相似,其實 spring-boot-starter-data-mongodb 和 Spring-boot-starter-data-jpa 都來自於 Spring Data 大家族,它們的實現原理基本一致,因此使用 Repository 完全可以參考前面課程 JPA 用法。

多數據源 MongoDB 的使用

我們來學習在多數據源的情況下,如何使用 MongoDB。

(1)添加配置

首先在配置文件中添加不同的數據源,配置文件添加兩條數據源,如下:

mongodb.primary.uri=mongodb://192.168.0.1:27017
mongodb.primary.database=primary
mongodb.secondary.uri=mongodb://192.168.0.1:27017
mongodb.secondary.database=secondary

如果使用的是 MongoDB 集羣可以這樣配置:

mongodb.xxx.uri=mongodb://192.168.0.1:27017,192.168.0.2:27017,192.168.0.3:27017
mongodb.xxx.database=xxx

(2)配置數據源

封裝讀取以 MongoDB 開頭的兩個配置文件:

@Data
@ConfigurationProperties(prefix = "mongodb")
public class MultipleMongoProperties {

    private MongoProperties primary = new MongoProperties();
    private MongoProperties secondary = new MongoProperties();
    //省略 getter、setter
}

創建 MultipleMongoConfig 類分別創建兩個數據源的 MongoTemplate。

@Configuration
public class MultipleMongoConfig {
    @Autowired
    private MultipleMongoProperties mongoProperties;
}

根據配置文件信息構建第一個數據源的 MongoDbFactory。

@Bean
@Primary
public MongoDbFactory primaryFactory(MongoProperties mongo) throws Exception {
    MongoClient client = new MongoClient(new MongoClientURI(mongoProperties.getPrimary().getUri()));
    return new SimpleMongoDbFactory(client, mongoProperties.getPrimary().getDatabase());
}

利用上面構建好的 MongoDbFactory 創建對應的 MongoTemplate 。

@Primary
@Bean(name = "primaryMongoTemplate")
public MongoTemplate primaryMongoTemplate() throws Exception {
    return new MongoTemplate(primaryFactory(this.mongoProperties.getPrimary()));
}

最後將 MongoTemplate 信息注入到對應的包路徑下:

@Configuration
@EnableConfigurationProperties(MultipleMongoProperties.class)
@EnableMongoRepositories(basePackages = "com.neo.repository.primary",
        mongoTemplateRef = "primaryMongoTemplate")
public class PrimaryMongoConfig {
}

以上第一個數據源就配置完成了,第二個數據源的配置過程類似,大家可以直接看課程演示項目。總結一下封裝過程:讀取配置信息封裝爲 Properties,根據 Properties 信息構建 Factory,再由 Factory 構建出最後需要使用的 MongoTemplate,MongoTemplate 在根據包路徑配置注入到對應的包下。

(3)創建兩個庫分別對應的對象和 Repository

public class User implements Serializable {
        private static final long serialVersionUID = -3258839839160856613L;
        private String  id;
        private String userName;
        private String passWord;
        //省略getter setter
}

對應的 Repository:

public interface PrimaryRepository extends MongoRepository<User, String> {
}

繼承了 MongoRepository 會默認實現很多基本的增、刪、改、查功能,省了很多自己寫 Dao 層的代碼,Secondary 和上面的代碼類似就不貼出來了。

(4)最後測試

@RunWith(SpringRunner.class)
@SpringBootTest
public class MuliDatabaseTest {
    @Autowired
    private PrimaryRepository primaryRepository;
    @Autowired
    private SecondaryRepository secondaryRepository;

    @Test
    public void TestSave() {
        this.primaryRepository.save(new User("小張", "123456"));
        this.secondaryRepository.save(new User("小王", "654321"));
        List<User> primaries = this.primaryRepository.findAll();
        for (User primary : primaries) {
            System.out.println(primary.toString());
        }
        List<User> secondaries = this.secondaryRepository.findAll();
        for (User secondary : secondaries) {
            System.out.println(secondary.toString());
        }
    }
}

輸出內容如下:

com.neo.entity.UserEntity@2a3a299[id=5a0ed2d7439f6618ac294ba6,userName=小張,passWord=123456]
com.neo.entity.UserEntity@5b989dc7[id=5a0ed2d7439f6618ac294ba7,userName=小王,passWord=654321]

使用 Robo 3T 分別查看兩個庫的數據,均已經有對應的 User 對象,說明多數據源使用測試成功。

總結

兩種方式都可以方便地操作 MongoDB 數據庫,MongoTemplate 模式比較靈活可以根據自己的使用情況進行組裝,MongoRepository 的使用方式更簡單,因爲繼承了 Spring Data,所以默認實現了很多基礎的增、刪、改、查功能,業務直接拿來使用即可。簡單日常的使用推薦 MongoRepository,簡單方便利於項目快速開發,複雜多變的查詢推薦使用 MongoTemplate 自行進行封裝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章