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

时间:2013-08-19 18:02:03

标签: java mongodb datetime jodatime

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

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

我正在使用的Java框架限制我使用字符串作为我的测试参数,因此我尝试将该字符串与DateTimeFormatter一起使用,以使其进入正确的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时间对象在禁止?我还查看了另一个建议,查看了像Spring Data这样的mapper框架。看起来还有很多东西需要进入这个。真的没有简单的方法可以将其持久化为mongo吗?

EDIT2:

好吧,我想我现在拥有它。我可能没有完全掌握所有的机制,但是BasicDBObjectDateTime不太好。日期对象似乎是唯一的方法,至少在我正在处理的实现中。我做了以下事情:

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

然后我传入了newResult for BasicDBObject,然后在mongo中更新记录。它工作正常,记录正确更新。

4 个答案:

答案 0 :(得分:7)

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

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

DateTimeFormatter parser = ISODateTimeFormat.dateTime();

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

DateTime result = parser.parseDateTime(crDt);

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

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

答案 1 :(得分:5)

我通过添加"编码挂钩"解决了这个问题。在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

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

}

答案 2 :(得分:3)

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

注意时区。与java.util.Date对象不同,Joda-Time中的DateTime对象确实知道自己分配的时区。您是否希望为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" ) );

答案 3 :(得分: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>

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

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

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.MongoClient中找不到它。如果您确定如何使用com.mongodb.client.MongoClient,请添加注释...