当前位置 : 首页 » 文章分类 :  开发  »  Spring-Data-MongoDB

Spring-Data-MongoDB

Spring Data MongoDB 使用笔记


SpringBoot整合MongoDB

MongoTemplate方式

SpringBoot 集成 Spring Data Mongodb 操作 MongoDB 详解
http://www.mydlq.club/article/85/

MongoRepository方式

spring-data-mongodb 中的实体映射是通过 MongoMappingConverter 这个类实现的。它可以通过注释把 java 类转换为 mongodb 的文档。

它有以下几种注解:
@Id 文档的唯一标识,在mongodb中为ObjectId,它是唯一的,通过时间戳+机器标识+进程ID+自增计数器(确保同一秒内产生的Id不会冲突)构成。
@Document 把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。@Document(collection=“mongodb”) mongodb对应表
@DBRef 声明类似于关系数据库的关联关系。ps:暂不支持级联的保存功能,当你在本实例中修改了DERef对象里面的值时,单独保存本实例并不能保存DERef引用的对象,它要另外保存。
@Indexed 声明该字段需要索引,建索引可以大大的提高查询效率。
@CompoundIndex 复合索引的声明,建复合索引可以有效地提高多字段的查询效率。
@GeoSpatialIndexed 声明该字段为地理信息的索引。
@Transient 映射忽略的字段,该字段不会保存到mongodb。
@PersistenceConstructor 声明构造函数,作用是把从数据库取出的数据实例化为对象。该构造函数传入的值为从DBObject中取出的数据

Mongo 3.0 Java driver不支持通过spring.data.mongodb.host配置连接

错误:
通过下面这三个参数配置 mongo 连接

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=app1

SpringBoot 启动时报错

2021-05-27 18:47:58.395 [cluster-ClusterId{value='60af78debbf59a7c9ef01bed', description='null'}-localhost:27017] INFO  org.mongodb.driver.cluster - Exception in monitor thread while connecting to server localhost:27017
com.mongodb.MongoSocketOpenException: Exception opening socket
    at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:70)
    at com.mongodb.internal.connection.InternalStreamConnection.open(InternalStreamConnection.java:128)
    at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:117)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.net.ConnectException: Connection refused (Connection refused)
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at com.mongodb.internal.connection.SocketStreamHelper.initialize(SocketStreamHelper.java:64)
    at com.mongodb.internal.connection.SocketStream.initializeSocket(SocketStream.java:79)
    at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:65

原因:
Mongo 3.0 Java driver 不支持通过 spring.data.mongodb.host 和 spring.data.mongodb.port 配置连接,必须使用 spring.data.mongodb.uri 配置连接参数。

If you use the Mongo 3.0 Java driver, spring.data.mongodb.host and spring.data.mongodb.port are not supported. In such cases, spring.data.mongodb.uri should be used to provide all of the configuration.

31.2.1 Connecting to a MongoDB Database
https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-nosql.html

解决:
改为通过 uri 配置连接则可以正常启动

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

Spring Data MongoDB可自动创建数据库

连接中配置的 MongoDB 数据库不存在也没关系,Spring Data MongoDB 可自动创建 DB, collection 当然更能自动创建了。
spring.data.mongodb.uri=mongodb://localhost:27017/test


Spring Data 连接 MongoDB开启认证

MongoDB 连接串格式:

mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]

注意:password 中的 @ 等符号需要做 url 编码,比如 @ 要替换为 %40

spring:
  data:
    mongodb:
      uri: mongodb://user:passwd@127.0.0.1:8017/dbname?authSource=admin

SpringBoot同时集成SpringDataJpa+MongoDB

项目中要同时使用 MySQL 和 MongoDB, 使用 Spring Data 统一对接,整合到一个统一的 persistent 持久化模块中,对其他模块只暴露 Service 层,屏蔽底层 Jpa 和 Mongo 差异。

定义Jpa和Mongo实体

这里我将 Jpa 和 Mongo 的实体类放在同一包下了。

Jpa 实体:

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "rules")
public class RulesDO extends DefaultDateDO {
    private String name;

    @ColumnDefault("'enabled'")
    private String status;
}

Mongo 实体:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "msg")
public class MessageDO {
    @Id
    private String id;

    private String name;
}

Jpa和Mongo的Repository

实际使用中,还会再包一层 Service 层,对外都通过 Service 层访问,这里略过,直接使用 Repository
这里我将 Jpa 和 Mongo 的 Repository 接口放在同一包下了。

Jpa 的 repository

@Repository
public interface RulesRepository extends JpaRepository<RulesDO, Long> {
}

Mongo 的 repository

@Repository
public interface MessageRepository extends MongoRepository<MessageDO, String> {
}

Jpa和Mongo配置类

Jpa 配置类, PersistenceRepositoryPackage 是 Repository 所在包的一个标记接口,也可以直接通过 basePackages 属性配置包路径。

@Configuration
@EnableJpaAuditing
@EnableJpaRepositories(basePackageClasses = {PersistenceRepositoryPackage.class})
public class JpaConfiguration {
}

Mongo 配置类

@Configuration
@EnableMongoRepositories(basePackageClasses = {PersistenceRepositoryPackage.class})
public class MongoConfiguration {
}

总的持久化配置

PersistenceDOPackage 是 Jpa 和 Mongo 实体类所在包的一个标记接口,也可以直接通过 basePackages 属性配置包路径。

@Configuration
@ComponentScan
@EntityScan(basePackageClasses = {PersistenceDOPackage.class})
@Import({JpaConfiguration.class, MongoConfiguration.class})
public class PersistenceConfiguration {
}

单测集成H2内存数据库及EmbedMongo

maven引入依赖

引入maven依赖,为了使用内存数据库需要引入 h2, 为了使用内存mongo需要引入 de.flapdoodle.embed.mongo 和 embedmongo-spring
SpringBoot 版本是 2.2.6.RELEASE, 默认是 JUnit5

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
    <groupId>de.flapdoodle.embed</groupId>
    <artifactId>de.flapdoodle.embed.mongo</artifactId>
    <version>1.50.5</version>
</dependency>
<dependency>
    <groupId>cz.jirutka.spring</groupId>
    <artifactId>embedmongo-spring</artifactId>
    <version>RELEASE</version>
</dependency>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

配置apt-maven-plugin插件同时支持jpa和mongo

apt-maven-plugin 是 QueryDSL 用来生成查询Q类的插件,需要改下配置来同时支持 Jpa 和 Mongo

<build>
    <plugins>
        <plugin>
            <groupId>com.mysema.maven</groupId>
            <artifactId>apt-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>jpa</id>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>process</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/generated-sources/java</outputDirectory>
                        <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                    </configuration>
                </execution>
                <execution>
                    <id>mongo</id>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>process</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/generated-sources/java</outputDirectory>
                        <processor>
                            org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor
                        </processor>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

单测yml配置

debug: true

spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
    username: sa
  jpa:
    database: h2
    show-sql: true
    properties:
      hibernate:
        format_sql: true

BaseTest单测基类

JUnit5 中不再使用 @RunWith(SpringRunner.class) 替换为 @ExtendWith(SpringExtension.class)

@SpringBootTest
@ExtendWith(SpringExtension.class)
public abstract class BaseTest {
    @SpringBootApplication
    @Import(PersistenceConfiguration.class)
    public static class TestApplication {
    }
}

Mongo单测

@Slf4j
public class MessageTest extends BaseTest {
    @Autowired
    private MessageRepository messageRepository;

    @Test
    public void testSaveAndFind() {
        MessageDO messageDO = MessageDO.builder().name("name").build();
        messageRepository.save(messageDO);
        log.info(JsonMappers.NonEmpty.toPrettyJson(messageDO));
        Assertions.assertNotNull(messageDO.getId());

        List<MessageDO> res = Lists.newArrayList(messageRepository.findAll(QMessageDO.messageDO.name.eq("name")));
        log.info(JsonMappers.NonEmpty.toPrettyJson(res));
        Assertions.assertTrue(res.size() >= 1);
    }
}

Jpa单测

Jpa 的测试可以分为两种方式:
1、加 @DataJpaTest 注解,不启动 Spring 上下文,只关注Jpa的测试,可以注入 TestEntityManager 使用。
2、使用传统的 @SpringBootTest 启动 Spring 上下文,注入 EntityManager 使用。

两种方式都可以使用 @AutoConfigureTestDatabase 注解,不需要准备 data.sql 初始化脚本,会自动根据 Jpa 实体初始化 h2 数据库。

Jpa单测基类

//@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class BaseJpaTest extends BaseTest {
    @Autowired
    private EntityManager entityManager;

    @BeforeEach
    public void setUp() {
        RulesDO rulesDO = new RulesDO();
        rulesDO.setName("测试");
        entityManager.persist(rulesDO);
    }
}

Jpa 单测类:

@Slf4j
@Transactional
public class RulesTest extends BaseJpaTest {
    @Autowired
    private RulesRepository rulesRepository;

    @Test
    public void testSaveAndFind() {
        RulesDO rulesDO = RulesDO.builder().name("测试").build();
        rulesRepository.save(rulesDO);
        Assertions.assertNotNull(rulesDO.getId());

        List<RulesDO> rulesDOList = Lists.newArrayList(rulesRepository.findAll(QRulesDO.rulesDO.name.eq("测试")));
        Assertions.assertTrue(rulesDOList.size() >= 1);
    }
}

Spring Boot with Embedded MongoDB
https://springframework.guru/spring-boot-with-embedded-mongodb/

Spring Boot Integration Testing with Embedded MongoDB
https://www.baeldung.com/spring-boot-embedded-mongodb


Spring Data设置MongoDB TTL索引(过期时间)

给索引注解 @Indexed 加 expireAfterSeconds 参数来设置过期时间。

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "msg")
public class MessageDO {
    @Id
    private String id;

    // code
    @Indexed(name = "msgCode")
    private String msgCode;

    // 过期时间
    @Indexed(name = "expireAt", expireAfterSeconds = 0)
    private Date expireAt;

    @CreatedDate
    private Date createdDate;

    @LastModifiedDate
    private Date lastModifiedDate;
}

运行时有如下提示:

2021-05-31 17:47:24.761 [main] WARN  o.s.d.m.c.i.MongoPersistentEntityIndexCreator.warnOnce:72 - Automatic index creation will be disabled by default as of Spring Data MongoDB 3.x.
    Please use 'MongoMappingContext#setAutoIndexCreation(boolean)' or override 'MongoConfigurationSupport#autoIndexCreation()' to be explicit.
    However, we recommend setting up indices manually in an application ready block. You may use index derivation there as well.

    > -----------------------------------------------------------------------------------------
    > @EventListener(ApplicationReadyEvent.class)
    > public void initIndicesAfterStartup() {
    >
    >     IndexOperations indexOps = mongoTemplate.indexOps(DomainType.class);
    >
    >     IndexResolver resolver = new MongoPersistentEntityIndexResolver(mongoMappingContext);
    >     resolver.resolveIndexFor(DomainType.class).forEach(indexOps::ensureIndex);
    > }
    > -----------------------------------------------------------------------------------------

Spring Data MongoDB 审计

Spring Data MongoDB 和 Jpa 类似,也支持在字段或者方法上进行注解 @CreateDate @CreatedBy @LastModifiedDate @LastModifiedBy

@CreateDate 表示该字段为创建时间时间字段,在这个实体被 insert 的时候,会设置默认值

@CreatedBy 表示该字段为创建人,在这个实体被 insert 的时候,会设置值。

指定 @EnableMongoAuditing 来启用JPA审计:

@Configuration
@EnableMongoAuditing
public class MongoConfiguration {
}

上一篇 MapStruct

下一篇 Spring-Cloud-Stream

阅读
评论
2.3k
阅读预计11分钟
创建日期 2021-05-24
修改日期 2022-03-10
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论