Java 使用 Joda-Time 为 Mongo 插入形成正确的 ISODate

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18320175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:27:54  来源:igfitidea点击:

Using Joda-Time to form correct ISODate for Mongo insert

javamongodbdatetimejodatime

提问by Benny

I am trying to update date fields in mongo that require an ISODate format. In mongo, it looks like this:

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

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

The Java framework I am using has me restricted to using strings as my test parameters, so I am trying to use that string with a DateTimeFormatterto get it into the correct ISODateTimeFormatand then pass that into mongo. I cannot just pass in a string that looks like what I have above. Trying to do so screws up the field in mongo. The relevant bits of Joda-Timecode I am using look like this:

我正在使用的 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);

And when the code runs I get errors like these from the .parseDateTime method:

当代码运行时,我从 .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)

I can tell the string I am giving is not correct to get things parsed. I've tried leaving off the Z, I've tried other combos, but each time it says it's malformed. So basically, what does my starting string need to be to get the .parseDateTimeto work and give me an object that looks correct?

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

EDIT:

编辑:

Updated to try the suggestions provided below. The issue I run into now is an IllegalArgumentException, can't serialize class org.joda.time.DateTime. So it appears persisting joda time objects in a no-go? I also looked at the other suggestion, looking into mapper frameworks like Spring Data. It looks like there is a whole lot more that needs to go into this. Is there really no simple way to persist this into mongo?

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

EDIT2:

编辑2:

OK, I think I have it now. I might not have a total grasp of all the mechanics at play, but BasicDBObjects won't play nice with DateTime. Date objects seem to be the only way to go, at least in the implementation I'm dealing with. I did the following:

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

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

I then passed in newResult for the BasicDBObject to then update the record in mongo. It works fine, and the record is updated correctly.

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

采纳答案by Matt Johnson-Pint

Your input string format is correct, as long is that is intended to represent UTC.

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

Change your parser to use the one that matches this format:

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

DateTimeFormatter parser = ISODateTimeFormat.dateTime();

The rest of your question doesn't make much sense to me. You shouldn't pass the parser, but rather the return value from parseDateTime, which you don't appear to be capturing.

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

DateTime result = parser.parseDateTime(crDt);

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

Whether or not that last line will work depends on what that function accepts.

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

回答by Basil Bourque

The answer by Matt Johnson is correct. But it could be even simpler: Pass the (ISO 8601) string directly to constructor of DateTime. No need for a formatter.

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

Pay attention to time zone. A DateTime object in Joda-Time truly knows its own assigned time zone, unlike a java.util.Date object. Do you want your DateTime object to be assigned the JVM's default time zone, no time zone (UTC), or a specific time zone?

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

For a date-time assigned the default time zone.

对于分配了默认时区的日期时间。

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

For a date-time assigned UTC/GMT (no time zone offset).

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

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

For a date-time assigned a specific time zone.

对于分配了特定时区的日期时间。

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

回答by Miguel Reyes

I solved this by adding an "Encoding Hook" in the constructor of the Service class where I do the updates to MongoDB. This will allow you to use org.joda.time.DateTime in your code and that will be saved as java.util.Date in MongoDB.

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

MyService.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

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");
    }

}

回答by barrypicker

Most of these answers are ancient. The Mongo driver is much newer and has changed dramatically. Here is an answer as of March 6, 2019 - using the most recent version of the Mongo Java driver - version 3.10.1, using most recent version of Joda time - 2.10.1. I intentionally use fully qualified class names so there is no confusion on the libraries in use. As such, there is no need for any import statements.

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

**

**

Edit 2019-03-09:

编辑 2019-03-09:

Feedback from user @OleV.V. (see comments below) indicate perhaps Joda Time is less favorable over the Java 8 java.time libraries. Upon investigation I find the current MongoDB Java driver supports the java.time.Instant.now() method properly and saves an ISODate without the need of a codec. The information provided here illustrates how to add a custom codec to the driver. For this reason alone I believe there is value in this answer.

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

**

**

My answer is derived from work from SquarePegSys BigDecimalCodec.java found at https://gist.github.com/squarepegsys/9a97f7c70337e7c5e006a436acd8a729, the difference is their solution is geared towards supporting big decimal values, my solution is geared toward Joda DateTime compatibility.

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

I like to provide the output of the program first, before showing source code. This way you can evaluate if the output is providing the solution you are seeking before investing time digesting and understanding the code. Again, the point is to save a date value as an ISODate datatype in mongoDB using Joda time, i.e., so the saved DateTime is not saved as a string.

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

I am using Maven to build. I am running Ubuntu 18.04LTS.

我正在使用 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"

Build the program:

构建程序:

cd <directory holding pom.xml file>
mvn package

Run the program:

运行程序:

$ 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

Query results using mongo shell:

使用 mongo shell 查询结果:

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

Source Code

源代码

There are a total of 4 classes implemented (the pom.xml file is just a build tool file)...

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

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

pom.xml

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>

Main.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);
    }
}

DateTimeCodec.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

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;
    }
}

DateTimeTransformer.java

日期时间转换器.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;
    }
}

Conclusion

结论

The java world seems to be gravitating towards Joda time. Its a nice library and provides relief for common date/time needs. My guess is Mongo will natively support this library but for now we must help it along.

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

Quick note:I attempted to use the most modern mongoDB classes, but in class Main.java, I refer to an older library method - com.mongodb.MongoClient.getDefaultCodecRegistry() as I could not find it in com.mongodb.client.MongoClient. If you identify how to use com.mongodb.client.MongoClient instead please add a comment...

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