JPA之集成mongodb

JPA已成为ORM事实的标准,已经深入人心。不清楚的可以参阅《JPA之Spring Data JPA

目录

1. 原生使用

1.1 官方文档

1.2 历史版本

1.3 基本用法演示

2. spring-data-mongodb

2.1 官方文档

2.2 环境要求

2.3 历史版本

2.4 基本用法演示

2.5 spring-boot项目

2.6 MongoTemplate

2.7 设计实现自增id


1. 原生使用

1.1 官方文档

mongo-java-driver

1.2 历史版本

当前版本最新为:3.8.2

基本特性

BSON Library

A standalone BSON library, with a new Codec infrastructure that you can use to build high-performance encoders and decoders without requiring an intermediate Map instance.

MongoDB Driver

An updated Java driver that includes the legacy API as well as a new generic MongoCollection interface that complies with a new cross-driver CRUD specification.

MongoDB Async Driver

A new asynchronous API that can leverage either Netty or Java 7's AsynchronousSocketChannel for fast and non-blocking IO.

Core driver

The MongoDB Driver and Async Driver are both built on top of a new core library, which anyone can use to build alternative or experimental high-level APIs.

3.8.2Reference | API
3.7.1Reference | API
3.6.4Reference | API
3.5.0Reference | API
3.4.3Reference | API
3.3.0Reference | API
3.2.2Reference | API
3.1.1Reference | API
3.0.4Reference | API
2.14.2Reference | API
2.13.3Reference | API

1.3 基本用法演示

先了解下MongoClient的用法

项目中引入

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.3.0</version>
</dependency>

基本使用

public class MongoDriver_test {
    private MongoClient mongo;
    private MongoDatabase db;
    private MongoCollection<Document> users;
    private String host = "127.0.0.1";
    private int port = 27017;
    private String dbname = "javadb";

    @Before
    public void init() {
        MongoClientOptions.Builder build = new MongoClientOptions.Builder();
        //与数据最大连接数50
        build.connectionsPerHost(50);
        //如果当前所有的connection都在使用中,则每个connection上可以有50个线程排队等待
        build.threadsAllowedToBlockForConnectionMultiplier(50);
        build.connectTimeout(1 * 60 * 1000);
        build.maxWaitTime(2 * 60 * 1000);
        MongoClientOptions options = build.build();
        //mongo = new MongoClient(host,options);
        mongo = new MongoClient(host, port);
        db = mongo.getDatabase(dbname);
        // 获取temp DB;如果默认没有创建,mongodb会自动创建
        // 获取 DBCollection;如果默认没有创建,mongodb会自动创建
        //users=db.createCollection("users");
        users = db.getCollection("users");

    }

    @After
    public void destory() {
        if (mongo != null) {
            mongo.close();
        }
        db = null;
    }

    @Test
    public void test_db() {
        //所有的db
        for (String name : mongo.getDatabaseNames()) {
            System.out.println("dbName:" + name);
        }
    }

    @Test
    public void test_queryAll() {
        MongoCursor<Document> cursor = users.find().iterator();
        iterator_dis(cursor);
    }

    private void iterator_dis(MongoCursor<Document> cursor) {
        while (cursor.hasNext()) {
            System.out.println(cursor.next());
        }
    }

    /**
     * 也是支持BasicDBObject的
     */
    @Test
    public void test_search() {
        System.out.println("find age <= 24:");
        MongoCursor<Document> cursor = users.find(Filters.lte("age", 24)).iterator();
        //MongoCursor<Document> cursor = users.find(new BasicDBObject("age", new BasicDBObject("$lte", 24))).iterator();
        iterator_dis(cursor);

        System.out.println("查询age not in 25/26/27:");
        cursor = users.find(new BasicDBObject("age", new BasicDBObject(QueryOperators.NIN, new int[]{25, 26, 27}))).iterator();
        iterator_dis(cursor);

        System.out.println("查询age exists 排序:");
        cursor = users.find(new BasicDBObject("age", new BasicDBObject(QueryOperators.EXISTS, true))).iterator();
        iterator_dis(cursor);

        System.out.println("findAndRemove 查询age=25的数据,并且删除: " + users.findOneAndDelete(new BasicDBObject("age", 20)));

        // users.findOneAndUpdate(new BasicDBObject("age", 28),)
    }

    @Test
    public void test_add() {
        Document user = new Document().append("name", "MongoDB").append("age", 20);
        Document address = new Document().append("city", "上海").append("area", "长宁");
        user.append("address", address);
        users.insertOne(user);
    }

    @Test
    public void test_remove() {
        Long result = users.deleteOne(Filters.eq("_id", new ObjectId("583fce65c181182f7c51df97"))).getDeletedCount();
        System.out.println("delete id:" + result);
        result = users.deleteMany(Filters.gte("age", 24)).getDeletedCount();
        System.out.println("delte all age>=24:" + result);
    }

    @Test
    public void test_modify() {
        long result = users.updateOne(Filters.eq("_id", new ObjectId("583fcb40c1811828bc8d4495")), new Document("$set",
                new Document("age", "26"))).getModifiedCount();
        System.out.println("updateMany:" + result);
    }

    @Test
    public void json_test() {
        DBObject user = new BasicDBObject();
        user.put("name", "lixiaolong");
        user.put("age", 25);
        //JSON 对象转换
        System.out.println("serialize:" + JSON.serialize(user));
        System.out.println("parse:" + JSON.parse("{ \"name\" : \"yaoming\" , \"age\" : 24}"));

    }
}

2. spring-data-mongodb

2.1 官方文档

官方文档

2.2 环境要求

The Spring Data MongoDB 2.x binaries require JDK level 8.0 and above and Spring Framework 4.3.20.RELEASE and above.
In terms of document stores, you need at least version 2.6 of MongoDB.

2.3 历史版本

Spring Data MongoDB 1.10

  1. Compatible with MongoDB Server 3.4 and the MongoDB Java Driver 3.4.
  2. New annotations for @CountQuery, @DeleteQuery, and @ExistsQuery.
  3. Extended support for MongoDB 3.2 and MongoDB 3.4 aggregation operators (see Supported Aggregation Operations).
  4. Support for partial filter expression when creating indexes.
  5. Publishing lifecycle events when loading or converting DBRef instances.
  6. Added any-match mode for Query By Example.
  7. Support for $caseSensitive and $diacriticSensitive text search.
  8. Support for GeoJSON Polygon with hole.
  9. Performance improvements by bulk-fetching DBRef instances.
  10. Multi-faceted aggregations using $facet, $bucket, and $bucketAuto with Aggregation.

Spring Data MongoDB 1.9

  1. The following annotations have been enabled to build your own composed annotations: @Document, @Id, @Field, @Indexed, @CompoundIndex, @GeoSpatialIndexed, @TextIndexed, @Query, and @Meta.
  2. Support for Projections in repository query methods.
  3. Support for Query by Example.
  4. Out-of-the-box support for java.util.Currency in object mapping.
  5. Support for the bulk operations introduced in MongoDB 2.6.
  6. Upgrade to Querydsl 4.
  7. Assert compatibility with MongoDB 3.0 and MongoDB Java Driver 3.2 (see: MongoDB 3.0 Support).

Spring Data MongoDB 1.8

  1. Criteria offers support for creating $geoIntersects.
  2. Support for SpEL expressions in @Query.
  3. MongoMappingEvents expose the collection name for which they are issued.
  4. Improved support for <mongo:mongo-client credentials="…​" />.
  5. Improved index creation failure error message.

Spring Data MongoDB 1.7

  1. Assert compatibility with MongoDB 3.0 and MongoDB Java Driver 3-beta3 (see: MongoDB 3.0 Support).
  2. Support JSR-310 and ThreeTen back-port date/time types.
  3. Allow Stream as a query method return type (see: Query Methods).
  4. GeoJSON support in both domain types and queries (see: GeoJSON Support).
  5. QueryDslPredicateExcecutor now supports findAll(OrderSpecifier<?>… orders).
  6. Support calling JavaScript functions with Script Operations.
  7. Improve support for CONTAINS keyword on collection-like properties.
  8. Support for $bit, $mul, and $position operators to Update.

2.4 基本用法演示

step1,pom中引入

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

step2,初始化spring配置

<?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: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/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">
    <!--已过时-->
    <!--<mongo:mongo id="mongo" host="127.0.0.1" port="27017"/>-->
    <!--<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">-->
    <!--<constructor-arg ref="mongo"/>-->
    <!--<constructor-arg value="javadb"/>-->
    <!--</bean>-->
    <mongo:mongo-client id="mongo" host="127.0.0.1" port="27017">
        <mongo:client-options connect-timeout="10000" />
    </mongo:mongo-client>
    <mongo:db-factory id="mongoDbFactory" dbname="javadb" mongo-ref="mongo"/>
    <!--<mongo:template db-factory-ref="mongoDbFactory"/>-->
    <mongo:repositories base-package="com.xfboy.mongo.dao"/>

    <!--解决save时_class问题-->
    <bean id="defaultMongoTypeMapper" class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
        <constructor-arg name="typeKey">
            <null/>
        </constructor-arg>
    </bean>
    <bean id="mappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext"/>
    <mongo:mapping-converter id="mappingMongoConverter" db-factory-ref="mongoDbFactory"
                             mapping-context-ref="mappingContext"
                             type-mapper-ref="defaultMongoTypeMapper"/>
    <mongo:template db-factory-ref="mongoDbFactory" converter-ref="mappingMongoConverter"/>
</beans>

step3,定义domain

@Document(collection = "phone")
public class Phone implements Serializable{
    @Id
    private String id;
    private String name;
    private String pic;
    private double price;

    public Phone() {
    }

    public Phone(String pic) {
        this.pic = pic;
    }

    public Phone(String id, String name, String pic, double price) {
        this.id = id;
        this.name = name;
        this.pic = pic;
        this.price = price;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", pic='" + pic + '\'' +
                ", price=" + price +
                '}';
    }
}

step4,定义Repository接口

/**
 * 此处命名准确,jpa规范(面向查询)即可定义处各种接口
 */
public interface PhoneRepository extends MongoRepository<Phone, String>, QueryByExampleExecutor<Phone> {
    Phone findById(String id);

    List<Phone> findByName(String name);
    Page<Phone> findAll(Pageable pageable);//获取所有数据,带分页排序
    Page<Phone> findByNameContaining(String name, Pageable pageable);

}

step5,让它运行起来

@ContextConfiguration(locations = "classpath*:mongo-config.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class PhoneRepository_Test {
    @Autowired
    private PhoneRepository phoneRepository;

    //灰常强大
    @Autowired
    private MongoTemplate mongoTemplate;

    //_class怎么去掉
    @Test
    public void test_add() {
        for (int i = 0; i < 8; i++) {
            Phone phone = new Phone(UUID.randomUUID().toString(), "iphone" + i + 1, "img" + i, 5000);
            phone = phoneRepository.insert(phone);
            System.out.println(phone);
        }
    }

    @Test
    public void test_delete() {
        //默认按id
        phoneRepository.delete(new Phone() {{
            setId("3");
        }});
    }

    //$set?????
    @Test
    public void test_modify() {
        Phone phone = phoneRepository.save(new Phone() {{
            setId("08ed2cb0-423d-4b98-9bf9-06314effb1f7");
            setPrice(5800);
        }});
        System.out.println(phone);
    }

    @Test
    public void test_findById() {
        Phone phone = phoneRepository.findById("4d09b6a5-20bc-4ca3-bcb1-df24a6384a45");
        System.out.println(phone);
    }

    @Test
    public void test_findByNameContaining() {
        List<Phone> result = phoneRepository.findByName("iphone");
        System.out.println(com.alibaba.fastjson.JSON.toJSON(result));
    }

    @Test
    public void test_count() {
        System.out.println(phoneRepository.count());
    }

    @Test
    public void test_query_Example() {
        //implement QueryByExampleExecutor
        Phone phone = new Phone() {{
            setName("iphone");
        }};
        Example<Phone> example = Example.of(phone, matching().withIncludeNullValues());
        System.out.println(phoneRepository.findAll(example).size());

        //example = Example.of(phone, matching().withMatcher("price", matcher -> matcher.transform(v -> Integer.valueOf(50))));

        example = Example.of(new Phone("IMG1"),
                matching().
                        withIgnorePaths("price").
                        withMatcher("pic", ignoreCase()));
        System.out.println(phoneRepository.count(example));

    }

    @Test
    public void test_query_Criteria() {
        Query query = new Query();
        query.addCriteria(new Criteria("name").is("iphone"));
        List<Phone> result = mongoTemplate.find(query, Phone.class);
        System.out.println(result.size());
    }

    @Test
    public void test_page() {
        Pageable pageable = new PageRequest(1, 2, Sort.Direction.DESC, "name");
        Page<Phone> page = phoneRepository.findByNameContaining("phone", pageable);
        System.out.println(com.alibaba.fastjson.JSON.toJSON(page));
        System.out.println(page.getTotalElements());//total rows
        System.out.println(page.getTotalPages());
        System.out.println(page.isFirst());
        System.out.println(page.isLast());
        System.out.println(page.hasNext());
    }
}

2.5 spring-boot项目

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

首先pom添加依赖,其实这个包本身没有任何spring boot启动项相关的代码,需要自己去编写。还是老的套路从spring.factories中进入分析得知关键实现类:

MongoAutoConfiguration:非常重要
EmbeddedMongoAutoConfiguration:可以忽略,属于内嵌模式运行
MongoDataAutoConfiguration:非常重要
MongoRepositoriesAutoConfiguration:非常重要

接下来就是yml配置:

spring:
    profiles: default
    data:
      mongodb:
        database: omsopa
        host: 192.168.1.25
        port: 28010
        username: mgomsopauser
        password: gillion168
        custom:
          connectionsPerHost: 200
          threadsAllowedToBlockForConnectionMultiplier: 10
          maxWaitTime: 60000
        repositories:
          enabled: false    #需关注下

还有一个也很重要,如何修改连接数据,超时之类的,这就需要拓展

@ConfigurationProperties(prefix = "spring.data.mongodb.custom")
public class MongoOptionsProperties {
    //每个主机答应的连接数(每个主机的连接池大小),当连接池被用光时,会被阻塞住
    private Integer connectionsPerHost=100;
    //如果当前所有的connection都在使用中,则每个connection上可以有5个线程排队等待
    private Integer threadsAllowedToBlockForConnectionMultiplier=5;
    //被阻塞线程从连接池获取连接的最长等待时间(ms)
    private Integer maxWaitTime=120000;
    //在建立(打开)套接字连接时的超时时间(ms)
    private Integer connectTimeout=10000;
    public Integer getConnectionsPerHost() {
        return connectionsPerHost;
    }

    public Integer getThreadsAllowedToBlockForConnectionMultiplier() {
        return threadsAllowedToBlockForConnectionMultiplier;
    }

    public void setThreadsAllowedToBlockForConnectionMultiplier(Integer threadsAllowedToBlockForConnectionMultiplier) {
        this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
    }

    public void setConnectionsPerHost(Integer connectionsPerHost) {
        this.connectionsPerHost = connectionsPerHost;
    }

    public Integer getMaxWaitTime() {
        return maxWaitTime;
    }

    public void setMaxWaitTime(Integer maxWaitTime) {
        this.maxWaitTime = maxWaitTime;
    }

    public Integer getConnectTimeout() {
        return connectTimeout;
    }

    public void setConnectTimeout(Integer connectTimeout) {
        this.connectTimeout = connectTimeout;
    }
}
//增强MongoClient客户端配置
@Configuration
@EnableConfigurationProperties(MongoOptionsProperties.class)
@EnableMongoRepositories(basePackages = "com.kxtx.opa.mongo")
public class MongoClientConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public MongoClientOptions mongoClientOptions(MongoOptionsProperties optionsProperties) {
        return MongoClientOptions.builder()
                .connectionsPerHost(optionsProperties.getConnectionsPerHost())
                .threadsAllowedToBlockForConnectionMultiplier(optionsProperties.getThreadsAllowedToBlockForConnectionMultiplier())
                .connectTimeout(optionsProperties.getConnectTimeout())
                .maxWaitTime(optionsProperties.getMaxWaitTime())
                .build();
    }
    /**
     * 解决保存_class问题
     */
    @Bean
    @ConditionalOnMissingBean({MongoConverter.class})
    public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory, CustomConversions conversions) {
        DefaultDbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
        mappingConverter.setCustomConversions(conversions);
        mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null, context));
        return mappingConverter;
    }
}

2.6 MongoTemplate

没错,这个是关键实现,想想RestTemplate、RedisTemplate、RetryTemplate。。。。它肯定也有一个方法execute(Callback input),O(∩_∩)O哈哈~,就是这个套路。以此为切入点,就很容易找到原始驱动(DB、DBCollection)

//MongoTemplate部分源码
public <T> T execute(DbCallback<T> action) {
        Assert.notNull(action);

        try {
            DB e = this.getDb();
            return action.doInDB(e);
        } catch (RuntimeException var3) {
            throw potentiallyConvertRuntimeException(var3, this.exceptionTranslator);
        }
    }

    public <T> T execute(Class<?> entityClass, CollectionCallback<T> callback) {
        return this.execute(this.determineCollectionName(entityClass), callback);
    }

    public <T> T execute(String collectionName, CollectionCallback<T> callback) {
        Assert.notNull(callback);

        try {
            DBCollection e = this.getAndPrepareCollection(this.getDb(), collectionName);
            return callback.doInCollection(e);
        } catch (RuntimeException var4) {
            throw potentiallyConvertRuntimeException(var4, this.exceptionTranslator);
        }
    }

2.7 设计实现自增id

这个在另一篇中有说明,请参阅《常见问题

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值