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

问题描述 投票:7回答:4

我正在尝试更新需要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吗?

Aaditi:

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

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

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

java mongodb datetime jodatime
4个回答
7
投票

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

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

DateTimeFormatter parser = ISODateTimeFormat.dateTime();

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

DateTime result = parser.parseDateTime(crDt);

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

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


5
投票

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

my service.Java

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

Jo大time transformer.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");
    }

}

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

1
投票

这些答案大多都是古老的。 Mongo驱动程序更新,并且发生了巨大变化。以下是截至2019年3月6日的答案 - 使用最新版本的Mongo Java驱动程序 - 版本3.10.1,使用最新版本的Joda时间 - 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.hml

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

DateTime codec.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;
    }
}

DateTime codec provider.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;
    }
}

DateTime transformer.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,请添加评论...

© www.soinside.com 2019 - 2024. All rights reserved.