我的情况是我有一个简单的POJO,里面有一个长字段,实际上是一个时间戳。
此字段必须位于由ISODate表示的mongo数据库中。
我可以在整个Pojo中编写一个转换器,但由于它只有25个中的一个字段没有多大意义,并且当字段发生变化或者另外一个字段发生变化时,它是另一个错误点。
有没有办法在默认转换服务之后调用默认转换器服务并更改另外两个服务,这将对性能产生很大影响。 或者是否有一个默认的Converter接口来覆盖?
这里最好的做法是什么?
也许有一个我可以应用于这个领域的注释?
是的,我也可以写一个LongToDate转换器,但这会影响所有POJO,而不仅仅是这一个。
这里有样本POJO:
public class Person {
private long someTimestamp;
//getters and setters
}
如果没有转换器,这将是这样的:
{
"_id" : ObjectId("52ae8eede4b0249cde22059e"),
"someTimestamp" : NumberLong(1392714950940)
}
该结果应如何:
{
"_id" : ObjectId("52ae8eede4b0249cde22059e"),
"someTimestamp" : ISODate("2013-12-23T23:00:00.000Z")
}
当这样的时间戳值在嵌套文档中时,所描述的问题变得更加复杂:
{
"_id" : ObjectId("52ae8eede4b0249cde22059e"),
"items" : [
"someTimestamp" : NumberLong(1392714950940)
]
}
POJO:
public class Person {
private Collection<OtherPojoThatHoldsTimestamps> items;
//getters and setters
}
也许我应该提到我使用Spring来实现这一目标。 (http://projects.spring.io/spring-data-mongodb/)
答案 0 :(得分:5)
听起来你需要注释驱动的转换。 reference manual中的第6.6.2节讨论了格式化程序的这一点,甚至还有一个带时间戳的例子。
一般的想法是,您要标记需要使用注释进行特殊处理的字段(它很好地使用了注释,您毕竟指定了元数据)。然后,您为具有该注释的字段注册转换器。
问题是我刚试过这个并且无法让它发挥作用,因为元数据正在迷失。我已经提交JIRA ticket来了解是否可以解决这个问题。
答案 1 :(得分:2)
解决方案之一是使用spring data mongodb listener:
package com.rc.user.auth.model.listener;
import com.mongodb.DBObject;
import com.nimbusds.jwt.JWTParser;
import com.rc.user.auth.model.OAuth2AccessTokenEntity;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
import org.springframework.stereotype.Component;
import java.text.ParseException;
@Component
public class OAuth2AccessTokenEntityListener extends AbstractMongoEventListener<OAuth2AccessTokenEntity> {
@Override
public void onBeforeSave(BeforeSaveEvent<OAuth2AccessTokenEntity> event) {
OAuth2AccessTokenEntity oat = event.getSource();
DBObject db = event.getDBObject();
db.put("tokenValue", oat.getJwt().serialize());
}
@Override
public void onAfterConvert(AfterConvertEvent<OAuth2AccessTokenEntity> event) {
OAuth2AccessTokenEntity oat = event.getSource();
DBObject db = event.getDBObject();
try {
oat.setJwt(JWTParser.parse(db.get("tokenValue").toString()));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
解决方案二是使用转换器:
package com.rc.user.auth.model.convert;
import com.nimbusds.jwt.JWT;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.WritingConverter;
@WritingConverter
public class JWTToStringConverter implements Converter<JWT, String> {
@Override
public String convert(JWT jwt) {
return jwt.serialize();
}
}
答案 2 :(得分:0)
您无法仅为一个班级定义Converter
。但是,在morphia将其重新映射到您的班级之前,您可以使用@PreLoad
来按摩该数据:
@PreLoad
void convertDate(final DBObject dbObject) {
dbObject.put("someTimestamp", new Date(dbObject.get("someTimestamp")));
}
有效的方法可以满足您的需求。这种方法有一些缺点。
@PreLoad
方法中的类似代码。希望这种情况不会经常发生,因此不应过于繁琐。希望这能让你开始朝着正确的方向前进。祝你好运。
答案 3 :(得分:0)
从 Spring Data MongoDB 3.2 开始,似乎无法轻松定义特定于字段的(非全局)类型转换器。
配置 bean 中的The type converters registered through the overriden configureConverters()
方法影响所有域对象。
但是您可以使用不同类型的字段和访问器方法(getter/setter)使用简单的解决方法来完成您想要的。
public class Person{
@AccessType(AccessType.Type.FIELD)
private Instant timestamp;
public long getTimestamp() {
return this.timestamp.getEpochSecond();
}
public void setTimestamp(final long ts) {
this.timestamp = Instant.ofEpochSecond(ts);
}
}
在上面的代码中,到 MongoDB 的持久化映射器将看到 java.time.Instant
类型
字段和 timestamp
字段将映射到 MongoDB 中的 Date
(ISODate
) 类型。
但是,其他 Java 程序将通过 Person.timestamp
类型访问 long
字段,因为 getter 和 setter 使用 long
类型。
虽然这种方法使用字段级注释(如果存在)不是那么花哨,但它简单直观。
java.time.Instant
是 Java 8 之后日期/时间必不可少的类之一。它似乎最适合 Java 中的时间戳。 Spring Data MongoDB 为 java.util.Date
和 java.time.Instant
到/从 MongoDB 的 date
(Date
, ISODate
) 类型提供了内置类型映射。
以下是我程序中的示例域对象。应用龙目岛。在域对象中,java.math.BigInteger
类型映射到 MongoDB 的 decimal
(Decimal128
) type。
@Getter
@Setter
@RequiredArgsConstructor
@Accessors(chain = true)
@Document(collection = "transactions")
public class Transaction implements java.io.Serializable{
@Id
private final String hash;
@Field(name = "block_no")
private long blockNo;
private long index;
private String from;
// denormalized field from account
@Field("from_is_contr")
private boolean fromIsContract;
private String to;
// denormalized field from account
@Field("to_is_contr")
private boolean toIsContract;
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
@AccessType(AccessType.Type.FIELD)
@Field(targetType = DECIMAL128)
private BigDecimal value;
public BigInteger getValue() {
return this.value.toBigInteger();
}
public Transaction setValue(BigInteger val) {
this.value = new BigDecimal(val);
return this;
}
@Indexed
private Instant at;
}