8

我正在尝试更新 mongo 中需要 ISODate 格式的日期字段。在 mongo 中,它看起来像这样:

"crDt" : ISODate("2013-08-19T17:21:57.549Z")

我正在使用的 Java 框架限制我使用字符串作为我的测试参数,所以我尝试使用该字符串与 aDateTimeFormatter使其正确ISODateTimeFormat,然后将其传递给 mongo。我不能只传递一个看起来像我上面的字符串。试图这样做会破坏 mongo 的领域。我正在使用的Joda-Time代码的相关位如下所示:

//I can't get this right.
String crDt = "2013-01-19T15:28:58.851Z";

DateTimeFormatter parser = ISODateTimeFormat.dateHourMinuteSecondMillis();

parser.parseDateTime(crDt);

// this method updates the record in mongo. This method totally works, so no 
// point in pasting it here, I just can't get the parser object correct to be 
// in the correct format once inserted, it needs to be the correct ISODate form.
mongo.setCrDt(recordId, parser);

当代码运行时,我会从 .parseDateTime 方法中得到类似的错误:

java.lang.IllegalArgumentException: Invalid format: "2013-01-19T15:28:58.851Z" is malformed at "Z"
    at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:866)

我可以告诉我给出的字符串不正确,无法解析。我试过Z去掉 ,我试过其他组合,但每次都说它格式不正确。所以基本上,我的起始字符串需要什么.parseDateTime才能工作并给我一个看起来正确的对象?

编辑:

更新以尝试下面提供的建议。我现在遇到的问题是 IllegalArgumentException,无法序列化类 org.joda.time.DateTime。所以它似乎在不可行的情况下坚持 joda time 对象?我还查看了其他建议,研究了 Spring Data 等映射器框架。看起来还有很多事情要做。真的没有简单的方法可以将其持久化到 mongo 中吗?

编辑2:

好的,我想我现在有了。我可能没有完全掌握游戏中的所有机制,但是BasicDBObject不会与DateTime一起玩得很好。日期对象似乎是唯一的方法,至少在我正在处理的实现中。我做了以下事情:

DateTimeFormatter parser = ISODateTimeFormat.dateTime();
DateTime result;
Date newResult;
result = parser.parseDateTime(crDt);
newResult = result.toDate();

然后我为 BasicDBObject 传入 newResult,然后更新 mongo 中的记录。它工作正常,并且记录已正确更新。

4

4 回答 4

7

您的输入字符串格式是正确的,只要它旨在表示 UTC。

更改您的解析器以使用与此格式匹配的解析器:

DateTimeFormatter parser = ISODateTimeFormat.dateTime();

你剩下的问题对我来说没有多大意义。您不应该传递parser,而是传递 from 的返回值parseDateTime,您似乎没有捕获它。

DateTime result = parser.parseDateTime(crDt);

mongo.setCrDt(recordId, result.toDate());

最后一行是否有效取决于该函数接受的内容。

于 2013-08-19T18:29:57.920 回答
5

我通过在我对 MongoDB 进行更新的服务类的构造函数中添加一个“编码挂钩”来解决这个问题。这将允许您在代码中使用 org.joda.time.DateTime,并将在 MongoDB 中保存为 java.util.Date。

我的服务.java

@Inject
public MyService(com.mongodb.Client client) {
      BSON.addEncodingHook(DateTime.class, new JodaTimeTransformer());
      BSON.addDecodingHook(Date.class, new JodaTimeTransformer());
      this.mongoClient = mongoClient;
}

JodaTimeTransformer.java

import java.util.Date;

import org.joda.time.DateTime;

public class JodaTimeTransformer implements org.bson.Transformer {

    @Override
    public Object transform(Object o) {
        if(o instanceof DateTime) {
            return ((DateTime)o).toDate();
        }
        else if(o instanceof Date) {
            return new DateTime((Date) o);
        }
        throw new IllegalArgumentException("JodaTimeTransformer can only be used with DateTime or Date");
    }

}
于 2014-04-17T11:23:20.967 回答
3

马特约翰逊的答案是正确的。但它可能更简单:将 ( ISO 8601 ) 字符串直接传递给DateTime的构造函数。不需要格式化程序。

注意时区。Joda-Time 中的 DateTime 对象真正知道自己分配的时区,这与 java.util.Date 对象不同。您是否希望为您的 DateTime 对象分配 JVM 的默认时区、无时区 (UTC) 或特定时区?

对于指定了默认时区的日期时间。

DateTime dateTime = new DateTime( "2013-01-19T15:28:58.851Z" );

对于指定的日期时间 UTC/GMT(无时区偏移)。

DateTime dateTime = new DateTime( "2013-01-19T15:28:58.851Z", DateTimeZone.UTC );

对于指定特定时区的日期时间。

DateTime dateTime = new DateTime( "2013-01-19T15:28:58.851Z", DateTimeZone.forId( "Europe/Paris" ) );
于 2014-01-06T05:43:13.780 回答
1

这些答案大多是古老的。Mongo 驱动程序更新了很多,并且发生了巨大的变化。这是截至 2019 年 3 月 6 日的答案 - 使用最新版本的 Mongo Java 驱动程序 - 版本 3.10.1,使用最新版本的 Joda time - 2.10.1。我故意使用完全限定的类名,这样就不会混淆正在使用的库。因此,不需要任何导入语句。

**

编辑 2019-03-09:

来自用户@OleV.V 的反馈。(见下面的评论)表明 Joda Time 可能不如 Java 8 java.time 库。经过调查,我发现当前的 MongoDB Java 驱动程序正确支持 java.time.Instant.now() 方法,并且无需编解码器即可保存 ISODate。此处提供的信息说明了如何将自定义编解码器添加到驱动程序。仅出于这个原因,我相信这个答案是有价值的。

**

我的答案来自于https://gist.github.com/squarepegsys/9a97f7c70337e7c5e006a436acd8a729上的 SquarePegSys BigDecimalCodec.java 的工作,不同之处在于他们的解决方案是针对支持大十进制值,我的解决方案是针对 Joda DateTime 兼容性。

我喜欢先提供程序的输出,然后再显示源代码。通过这种方式,您可以在投入时间消化和理解代码之前评估输出是否提供了您正在寻找的解决方案。同样,重点是使用 Joda 时间在 mongoDB 中将日期值保存为 ISODate 数据类型,即,保存的 DateTime 不会保存为字符串。

我正在使用 Maven 构建。我正在运行 Ubuntu 18.04LTS。

$ mvn -version
Apache Maven 3.5.2
Maven home: /usr/share/maven
Java version: 10.0.2, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-43-generic", arch: "amd64", family: "unix"

构建程序:

cd <directory holding pom.xml file>
mvn package

运行程序:

$ java -jar Test.jar 
Mar 06, 2019 5:12:02 PM com.mongodb.diagnostics.logging.JULLogger log
INFO: Cluster created with settings {hosts=[127.0.0.1:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
Mar 06, 2019 5:12:03 PM com.mongodb.diagnostics.logging.JULLogger log
INFO: Opened connection [connectionId{localValue:1, serverValue:9}] to 127.0.0.1:27017
Mar 06, 2019 5:12:03 PM com.mongodb.diagnostics.logging.JULLogger log
INFO: Monitor thread successfully connected to server with description ServerDescription{address=127.0.0.1:27017, type=STANDALONE, state=CONNECTED, ok=true, version=ServerVersion{versionList=[4, 0, 6]}, minWireVersion=0, maxWireVersion=7, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=3220919}
Mar 06, 2019 5:12:03 PM com.mongodb.diagnostics.logging.JULLogger log
INFO: Opened connection [connectionId{localValue:2, serverValue:10}] to 127.0.0.1:27017

使用 mongo shell 查询结果:

MongoDB > db.testcollection.find().pretty()
{
        "_id" : ObjectId("5c806e6272b3f469d9969157"),
        "name" : "barry",
        "status" : "cool",
        "number" : 1,
        "date" : ISODate("2019-03-07T01:05:38.381Z")
}

源代码

一共实现了4个类(pom.xml文件只是一个构建工具文件)...

  1. 主要的
  2. 变压器
  3. 提供者
  4. 编解码器

pom.xml

<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>test.barry</groupId>
    <artifactId>test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>test</name>
    <url>http://maven.apache.org</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${basedir}</outputDirectory>
                            <finalName>Test</finalName>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>test.barry.Main</mainClass>
                                </transformer>
                            </transformers>
                            <createDependencyReducedPom>false</createDependencyReducedPom>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongo-java-driver</artifactId>
            <version>3.10.1</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.1</version>
        </dependency>
    </dependencies>
</project>

主.java

package test.barry;

public class Main {

    public static void main(String[] args) {

        java.util.ArrayList<com.mongodb.ServerAddress> hosts = new java.util.ArrayList<com.mongodb.ServerAddress>();
        hosts.add(new com.mongodb.ServerAddress("127.0.0.1", 27017));

        com.mongodb.MongoCredential mongoCredential = com.mongodb.MongoCredential.createScramSha1Credential("testuser", "admin", "mysecret".toCharArray());

        org.bson.BSON.addEncodingHook(org.joda.time.DateTime.class, new test.barry.DateTimeTransformer());
        org.bson.codecs.configuration.CodecRegistry codecRegistry = org.bson.codecs.configuration.CodecRegistries.fromRegistries(
                org.bson.codecs.configuration.CodecRegistries.fromProviders(
                    new test.barry.DateTimeCodecProvider()
                ), com.mongodb.MongoClient.getDefaultCodecRegistry()
        );

        com.mongodb.MongoClientSettings mongoClientSettings = com.mongodb.MongoClientSettings.builder()
            .applyToClusterSettings(clusterSettingsBuilder -> clusterSettingsBuilder.hosts(hosts))
            .credential(mongoCredential)
            .writeConcern(com.mongodb.WriteConcern.W1)
            .readConcern(com.mongodb.ReadConcern.MAJORITY)
            .readPreference(com.mongodb.ReadPreference.nearest())
            .retryWrites(true)
            .codecRegistry(codecRegistry)
            .build();

        com.mongodb.client.MongoClient client = com.mongodb.client.MongoClients.create(mongoClientSettings);
        com.mongodb.client.MongoDatabase db = client.getDatabase("testdb");
        com.mongodb.client.MongoCollection<org.bson.Document> collection = db.getCollection("testcollection");

        // BUILD UP A DOCUMENT
        org.bson.Document document = new org.bson.Document("name", "barry")
            .append("status", "cool")
            .append("number", 1)
            .append("date", new org.joda.time.DateTime());

        collection.insertOne(document);
    }
}

日期时间编解码器.java

package test.barry;

public class DateTimeCodec implements org.bson.codecs.Codec<org.joda.time.DateTime> {
    @Override
    public void encode(final org.bson.BsonWriter writer, final org.joda.time.DateTime value, final org.bson.codecs.EncoderContext encoderContext) {
        writer.writeDateTime(value.getMillis());
    }

    @Override
    public org.joda.time.DateTime decode(final org.bson.BsonReader reader, final org.bson.codecs.DecoderContext decoderContext) {
        return new org.joda.time.DateTime(reader.readDateTime());
    }

    @Override
    public Class<org.joda.time.DateTime> getEncoderClass() {
        return org.joda.time.DateTime.class;
    }
}

DateTimeCodecProvider.java

package test.barry;

public class DateTimeCodecProvider implements org.bson.codecs.configuration.CodecProvider {
    @Override
    public <T> org.bson.codecs.Codec<T> get(final Class<T> classToVerify, final org.bson.codecs.configuration.CodecRegistry registry) {
        if (classToVerify == org.joda.time.DateTime.class) {
            return (org.bson.codecs.Codec<T>) new DateTimeCodec();
        }

        return null;
    }
}

日期时间转换器.java

package test.barry;

public class DateTimeTransformer implements org.bson.Transformer {
    @Override
    public Object transform(Object objectToTransform) {
        org.joda.time.DateTime value = (org.joda.time.DateTime) objectToTransform;
        return value;
    }
}

结论

Java 世界似乎被 Joda 时代所吸引。它是一个不错的库,可以缓解常见的日期/时间需求。我的猜测是 Mongo 会原生支持这个库,但现在我们必须帮助它。

快速说明: 我尝试使用最现代的 mongoDB 类,但在 Main.java 类中,我引用了一个较旧的库方法 - com.mongodb.MongoClient.getDefaultCodecRegistry(),因为我在 com.mongodb.client 中找不到它。蒙古客户。如果您确定如何使用 com.mongodb.client.MongoClient 代替,请添加评论...

于 2019-03-07T01:37:55.677 回答