java用spring-data-mongodb操作mongodb的时间问题

本文介绍了在使用Java的Spring Data MongoDB操作时遇到的时间查询问题,特别是时间在查询条件中自动提前8小时的疑惑。经过源码调试发现,问题在于时区转换,最终确定是由于BasicDBObject在toString方法中处理Date时将时区设置为格林时间,导致显示错误。解决方案是理解并正确处理时区问题,以避免类似困扰。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近项目用到了mongodb,做了个时间段查询,自己调试的时候,发现把时间放到查询条件类QueryBuilder中之后就会自动向前推8小时。。纠结了好几天,终于找到答案,这里跟大家分享一下,希望大家会避免。

先贴出主要代码:

// 构建查询条件
QueryBuilder queryBuilder = new QueryBuilder();
Date startDate = DateUtil.fromStringToDate("yyyy-MM-dd HH:mm:ss", "2017-01-07 10:00:00");
queryBuilder.put("createTime").greaterThanEquals(startDate);

// 要查询的字段
BasicDBObject fieldsObject = new BasicDBObject();
fieldsObject.put("name", 1);
fieldsObject.put("age", 1);
fieldsObject.put("createTime", 1);

// 执行查询
Query query = new BasicQuery(queryBuilder.get(), fieldsObject);
List<Map> list = mongoTemplate.find(query, Map.class, COLLECTION);

当queryBuilder.put(“createTime”).greaterThanEquals(startDate);执行之后,debug调试查看queryBuilder会发现startDate的值向前推了8小时,百度了半天发现是时区的问题,可是也没有具体解答,只好看源码跟进问题。

一开始以为是QueryBuilder的greaterThanEquals等方法做了时区的转换,于是找出来greaterThanEquals方法的源码:

    public QueryBuilder greaterThanEquals(Object object) {
        addOperand(QueryOperators.GTE, object);
        return this;
    }

    //里面做了一个addOperand方法,我们看addOperand的源码
    private void addOperand(String op, Object value) {
        if(op == null) {
            if (_hasNot) {
                value = new BasicDBObject(QueryOperators.NOT, value);
                _hasNot = false;
            }
            // 这里预警,put之后_query里的时间就变了
            _query.put(_currentKey, value);
            return;
        }

        Object storedValue = _query.get(_currentKey);
        BasicDBObject operand;
        if(!(storedValue instanceof DBObject)) {
            operand = new BasicDBObject();
            if (_hasNot) {
                DBObject notOperand = new BasicDBObject(QueryOperators.NOT, operand);
                _query.put(_currentKey, notOperand);
                _hasNot = false;
            } else {
                _query.put(_currentKey, operand);
            }
        } else {
            operand = (BasicDBObject)_query.get(_currentKey);
            if (operand.get(QueryOperators.NOT) != null) {
                operand = (BasicDBObject) operand.get(QueryOperators.NOT);
            }
        }
        operand.put(op, value);
    }

源码跟进来之后,发现是 _query.put(_currentKey, value);这句之后时间就会变,大家看图,我们先看传进去的时间,2017年01月10日10点整,如下图:
这里写图片描述

再来看put之后的_query值,下图:
这里写图片描述
可以看到,变成了2017年1月7日2点,于是又把问题放到了_query上面,看他的put里干了什么,我们要知道_query是什么类型。继续看源码:

public QueryBuilder() {
        _query = new BasicDBObject();
    }

初始化的时候创建了BasicDBObject对象,继续到BasicDBObject中看put方法,如下:

public class BasicBSONObject extends LinkedHashMap<String,Object> implements BSONObject {
    ...
    public Object put( String key , Object val ){
        return super.put( key , val );
    }
    ...
}

到这也不用看了,发现put方法是直接用的父类,点进去是HashMap类的put方法。。。到这线索没了,已疯。。。左思右想,spring-data-mongo肯定不会这么坑,一定是我的打开方式不对,于是想先往BasicDBObject中put个时间值,再把他get出来,看看get出来的值还改变吗,结果get出来的值没有向前推8小时,没有没有没有。

为了进一步证实调用数据库查询时用的是我们传进去的时间,还是向前推8小时之后的时间,我在mongodb中插入一条时间为2017-01-10 10:00:00的数据,然后在java中传2017-01-10 10:00:00这个时间条件进行查询,可以看到put之后仍然变成了2017-01-10 02:00:00,可是查询结果确实是查到了时间为2017-01-10 10:00:00的数据。

可以看到,这与我们的期望值一致,可是中间出了些问题,纠结了一段时间,突然想到会不会是toString的时候把时区改了呢,导致打印和显示的时候出来的是格林时间,事实证明真的是这样,BasicDBObject在toString方法中有个处理java.util.Date的地方,把时区设置成了格林时间,如下图:

现在我该说什么。。我们眼睛看到的,未必是真实的。。。

至此,时间的问题就分析完了,分享给大家,希望大家以后遇到不要绕弯路~第一次写博客,有点紧张~有什么不对欢迎指正~~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值