MongoDB系列三(Spring集成方案).

一、前言 

MongoDB是最为流行的开源文档数据库之一。Spring Data MongoDB提供了三种方式在Spring应用中使用MongoDB:

  • 通过注解实现对象-文档映射;
  • 使用MongoTemplate实现基于模板的数据库访问;
  • 自动化的运行时Repository生成功能。

二、集成实现

  • 启用MongoDB

    为了有效的使用Spring Data MongoDB,我们需要在Spring配置中添加几个必要的bean。首先,我们需要配置MongoClient,用它来创建Mongo实例,以便于访问MongoDB数据库。在这里,我们使用Spring Data MongoDB的MongoFactoryBean更加简单。因为它是一个工厂bean,会负责构建Mongo实例,而且不用处理MongoClient构造器所抛出的UnknownHostException异常。同时,我们还需要有一个MongoTemplate bean,实现基于模板的数据库访问。此外,不是必须,但是强烈推荐启用Spring Data MongoDB的自动化Repository生成功能。

1、pom.xml

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>1.10.7.RELEASE</version>
        </dependency>

2、MongoConfig.java 配置类配置方式

@Configuration
@EnableMongoRepositories(basePackages = "org.springframework.data.mongodb",repositoryImplementationPostfix = "Impl") //启用MongoDB的Repository功能
@ComponentScan(basePackages = "org.springframework.data.mongodb")
@PropertySource("classpath:mongo.properties")
public class MongoConfig {

    /*
    * MongoClientFactoryBean 工厂bean 会负责创建Mongo实例。
    * */
    @Bean(name = "mongo")
    public MongoClientFactoryBean mongoClientFactoryBean(Environment env) {
        MongoClientOptions.Builder builder = MongoClientOptions.builder();
        MongoClientOptions build = builder.build();
        MongoCredential credential = MongoCredential.createCredential(
                env.getProperty("mongo.user", String.class),
                env.getProperty("mongo.database", String.class),
                env.getProperty("mongo.password", String.class).toCharArray());
        MongoClientFactoryBean mongoClientFactoryBean = new MongoClientFactoryBean();
        mongoClientFactoryBean.setHost(env.getProperty("mongo.host", String.class));
        mongoClientFactoryBean.setPort(env.getProperty("mongo.port", Integer.class));
        mongoClientFactoryBean.setCredentials(new MongoCredential[]{credential});
        mongoClientFactoryBean.setMongoClientOptions(build);
        return mongoClientFactoryBean;
    }

    @Bean(name = "mongoTemplate")
    public MongoTemplate mongoTemplate(Mongo mongo, Environment env) {
        return new MongoTemplate(mongo, env.getProperty("mongo.database", String.class));
    }
}

3、applicationContext.xml XML配置方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mongo="http://www.springframework.org/schema/data/mongo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">

    <!--提供自动注入配置的扫描包-->
    <context:component-scan base-package="org.springframework.data.mongodb"/>

    <context:property-placeholder location="classpath:mongo.properties" file-encoding="utf-8"/>

    <!--
    1.mongo:连接配置
    2.db-factory:相当于sessionFactory
    3.mongoTemplate:与数据库接口交互的主要实现类
    -->

    <mongo:mongo-client id="mongoClient" host="${mongo.host}" port="${mongo.port}" credentials="${mongo.user}:${mongo.password}@${mongo.database}">
        <mongo:client-options
                min-connections-per-host="${mongo.minConnectionsPerHost}"
                threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}"
                connect-timeout="${mongo.connectTimeout}"
                max-wait-time="${mongo.maxWaitTime}"
                socket-keep-alive="${mongo.socketKeepAlive}"
                socket-timeout="${mongo.socketTimeout}"
                max-connection-idle-time="${mongo.maxConnectionIdleTime}"
                max-connection-life-time="${mongo.maxConnectionLifeTime}"
                heartbeat-socket-timeout="${mongo.heartbeatSocketTimeout}"
                heartbeat-connect-timeout="${mongo.heartbeatConnectTimeout}"
                min-heartbeat-frequency="${mongo.minHeartbeatFrequency}"
                heartbeat-frequency="${mongo.heartbeatFrequency}"/>
    </mongo:mongo-client>

    <mongo:db-factory id="mongoDbFactory" dbname="${mongo.database}" mongo-ref="mongoClient"/>

    <!-- Spring提供的mongodb操作模板-->
    <mongo:template id="mongoTemplate" db-factory-ref="mongoDbFactory" write-concern="NORMAL"/>

    <!-- mongodb bean的仓库目录,会自动扫描扩展了MongoRepository接口的接口进行注入 -->
    <mongo:repositories base-package="org.springframework.data.mongodb" repository-impl-postfix="Impl"/>
</beans>
  • 注解实现对象文档映射

    Spring Data MongoDB 提供了一套对象-文档 映射的注解。

@Document - 用于类,以表示这个类需要映射到数据库,您也可以指定映射到数据库的集合名称 @Id - 用于字段级别,标记这个字段是一个主键,默认生成的名称是“_id” @DBRef - 用于字段,以表示它将使用com.mongodb.DBRef进行存储。 @Indexed - 用于字段,表示该字段需要如何创建索引 @CompoundIndex - 用于类,以声明复合索引 @GeoSpatialIndexed - 用于字段,进行地理位置索引 @TextIndexed - 用于字段,标记该字段要包含在文本索引中 @Field - 用于字段,并描述字段的名称,因为它将在MongoDB BSON文档中表示,允许名称与该类的字段名不同。 @Version - 用于字段锁定,保存操作时检查修改。初始值是0,每次更新时自动触发。 @Language - 用于字段,以设置文本索引的语言覆盖属性。 @Transient - 默认情况下,所有私有字段都映射到文档,此注解将会去除此字段的映射 @PersistenceConstructor - 标记一个给定的构造函数,即使是一个protected修饰的,在从数据库实例化对象时使用。构造函数参数通过名称映射到检索的DBObject中的键值。

@Document
public class Order {

    /**
     * @ID 生成MongoDB文档的_id 内容,如果不指定,MongoDB 会主动生成一个
     */
    @Id
    private String id;

    /**
     * @Field 映射成MongoDB文档的字段内容
     */
    @Field("client")
    private String customer;

    /**
     * @Indexed 是否在该字段上加上索引
     */
    @Indexed
    private String type;
    

    /**
     * 集合类型最好使用 ? 不确定类型(或者说任意类型)
     * 否则会info(Found cycle for field 'itemList' in type 'Order' for path '')表明你的代码中有潜在的循环使用
     * 
     * 像这样有另一个对象的集合,另一个对象不用加任何的MongoDB 注释
     */
    private List<?> itemList = new ArrayList();
    
}

三、MongoOperations

    我们已经配置好了MongoTemplate,接下来,需要做的就是将其注入到使用它的地方。注意,在这里我们将MongoTemplate注入到一个类型为MongoOperations的属性中。MongoOperations 是 MongoTemplate 所实现的接口,不直接使用具体实现是一个好的习惯。

1、 MongoOperations 暴露了多个使用MongoDB文档数据库的方法。这里介绍几个最为常用的操作:

  • 计算集合的数量
        long order = mongoOperations.getCollection("order").count();
  • 保存文档
        Order order = new Order();
        mongoOperations.save(order, "order"); 
  • 根据文档的 _id 查找文档
        Order byId = mongoOperations.findById("5abb2a6303238760a48e3fd2", Order.class);
  • 得到所有文档
        List<Order> all = mongoOperations.findAll(Order.class);
  • 删除文档
        Order order = new Order();
        order.setId("1");
        mongoOperations.remove(order);

2、 不过 MongoOperations 最常见的用法还是接受一个 Query 对象作为参数进行查询、修改、删除的操作。这里简单介绍一些 Query 和 Criteria 的语法:

  • db.order.find({"client":"customer"})
        Criteria criteria = Criteria.where("client").is("customer");
        Query query = new Query(criteria);
        List<Order> orders = mongoOperations.find(query, Order.class);
  • db.order.find({"client":"customer","type":"豪华型"})
        Criteria criteria = Criteria.where("client").is("customer");
        criteria.and("type").is("豪华型");
        Query query = new Query(criteria);
  • db.order.find({"type":/^豪华/}) 正则表达式
        Criteria criteria = Criteria.where("client").is("customer");
        criteria.and("type").regex("^豪华");
        Query query = new Query(criteria);
  • db.order.find({"client":"customer"}).sort({"type":-1}) 排序
        Criteria criteria = Criteria.where("client").is("customer");
        Sort sort = new Sort(Sort.Direction.DESC,"type");
        Query query = new Query(criteria).with(sort);
        List<Order> orders = mongoOperations.find(query, Order.class);
  • db.order.find({"client":"customer"}).skip(5).limit(5) 分页
        Criteria criteria = Criteria.where("client").is("customer");
        /*limit 是pageSize , skip 是 第几页*pageSize */
        Query query = new Query(criteria).skip(5).limit(5);
        List<Order> orders = mongoOperations.find(query, Order.class);
  • 大于 小于 不等于
        Criteria criteria = Criteria.where("client").is("customer");
        criteria.and("key").lt(""); //小于
        criteria.and("key").lte(""); //小于等于
        criteria.and("key").gt(""); //大于
        criteria.and("key").gte(""); //大于等于
        criteria.and("key").ne(""); //不等于 mongoDB 没有 eq(等于) 这个操作
        Query query = new Query(criteria);
  • $in $size $elemMatch $exists
        List<String> list = new ArrayList();
        Criteria condition = Criteria.where("x").lt(10).and("x").gt(5);
        Criteria criteria = Criteria.where("client").is("customer");
        criteria.and("key").in(list);
        criteria.and("key").size(3); //匹配key数组长度等于 3 的文档
        criteria.elemMatch(condition); //要求 x 的数组每个元素必须同时满足 大于5 小于10
        criteria.and("key").exists(true);
        Query query = new Query(criteria);

 3、 MongoOperations 还有许多聚合函数、地理空间 的用法......这里就不介绍了,接下来的文章会提到。

四、MongoDB Repository

    Spring Data JPA Repository 有一个神奇的功能 —— 创建一个接口,我们只要按照一定的命名规则编写接口的方法,Spring Data JPA能够自动创建接口的实现。Spring Data MongoDB 当然也有这个特性,让我们来看看怎么实现吧!

    我们已经通过@EnableMongoRepositories注解启用了Spring Data MongoDB的Repository功能(或者通过xml配置的方式),接下来需要做的就是创建一个接口,Repository实现要基于这个接口来生成。不过,在这里,我们不再扩展JpaRepository,而是要扩展MongoRepository。

public interface OrderRepository extends MongoRepository<Order, String> {
    /**
     * 根据customer从文档中获取Order集合
     * @param customer
     * @return
     */
    //@Query会接受一个JSON查询,而不是JPA查询。?0 表示第一个参数,?1 表示第二个参数,以此类推
    // find这个查询动词并不是固定的。如果喜欢的话,我们还可以使用get作为查询动词:
    @Query("{'customer':?0,'type':'type'}")
    List<Order> findByCustomer(String customer);

    /**
     * 根据customer 和 type 从文档中获取Order集合
     * @param customer
     * @param type
     * @return
     */
    List<Order> findByCustomerAndType(String customer, String type);

    /**
     * 根据customer 和 type 从文档中获取Order集合(customer 在对比的时候使用的是like 而不是equals)
     * @param customer
     * @param type
     * @return
     */
    List<Order> findByCustomerLikeAndTypeLike(String customer, String type);

}

    既然扩展了 MongoRepository 接口,OrderRepository 自然而然的有了许多对Order文档进行CRUD操作的方法实现。

    像这种用法怎么用呢?比如我们前面要查询一个文档,很自然的写了一个Query条件用来查询。但是我们现在不用了,定义一个接口方法就可以了!连实现都不用!因为 Spring Data JPA 能够自动创建接口的实现。

    上面的代码用了@Query 注解。@Query注解可以为Repository方法指定自定义的查询。@Query能够像在JPA中那样用在MongoDB上。唯一的区别在于针对MongoDB时,@Query会接受一个JSON查询,而不是JPA查询。

五、结语

    之前单纯的以为MongoDB只是一个像Oracle、MySQL那样存储数据的数据库。今天才发现自己犯了个大大的错误,像市面上的打车软件的范围派单、叫餐软件的附近商家,都是通过MongoDB 的一个查询就搞定了。MongoDB 提供了很多地理位置逻辑的API......感觉又发现了一块新大陆呀!

    源代码地址:https://github.com/JMCuixy/SpringDataMongoDB

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏极客编程

用Java为Hyperledger Fabric(超级账本)开发区块链智能合约链代码之部署与运行示例代码

您已经定义并启动了本地区块链网络,而且已构建 Java shim 客户端 JAR 并安装到本地 Maven 存储库中,现在已准备好在之前下载的 Hyperled...

28210
来自专栏java相关

SpringBoot中Async异步方法和定时任务介绍

21940
来自专栏JackieZheng

学习SpringMVC——说说视图解析器

  各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~)   相信大家在昨天那篇如何获取...

271100
来自专栏公众号_薛勤的博客

Spring+Struts2+Hibernate框架整合流程

1.新建Maven项目,导入相关依赖(推荐) 若不使用maven:请前往Maven官网依次下载jar包导入)

10530
来自专栏Hadoop实操

如何使用Java代码访问CDH的Solr服务

CDH集群使用的Solr版本为4.10.3,Java开发中会经常使用到solrj客户端包访问Solr集群。本篇文章主要讲述如何使用Java代码访问Kerbero...

1.1K60
来自专栏芋道源码1024

【追光者系列】HikariCP 源码分析之 allowPoolSuspension

摘要: 原创出处 https://mp.weixin.qq.com/s/-WGg22lUQU41c_8lx6kyQA 「渣渣王子」欢迎转载,保留摘要,

16600
来自专栏编码前线

在Android项目中调用FFmpeg命令

34820
来自专栏JMCui

再学习之Spring(依赖注入).

一、概述     Spring框架是以 简化Java EE应用程序的开发 为目标而创建的。Spring可以实现很多功能,但是这些功能的底层都依赖于它的两个核心特...

38460
来自专栏程序猿DD

【译】Spring 官方教程:创建批处理服务

原文:Creating a Batch Service 译者:Mr.lzc 校对:lexburner 本指南将引导你完成创建基本的批处理驱动解决方案的过程。 你...

55170
来自专栏Android相关

Gradle For Android(7)--创建Task以及Plugin

到目前为止,我们已经看到了很多Gradle构建的属性,并且知道了怎么去执行Tasks。这一章,会更多的了解这些属性,并且创建我们自己的Task。一旦知道如何自定...

17720

扫码关注云+社区

领取腾讯云代金券