将SQL Server日期数据导入Joda对象的最佳方法是什么?

时间:2012-02-17 20:07:18

标签: java sql-server-2008 jodatime

TL; DR版本:请确认(如果没有,请提供帮助)我正确地将SQL Server datetimeoffset数据存入我的Joda Time Java对象。


我正在计划将数据库和Java代码移动到时区。为了实现这一目标,我已经全身心地this post并尝试实施最佳实践。请注意,所有代码都被视为“丢弃”代码,所以我并不关心这里的效率;只是正确性。

我们的环境由Microsoft SQL Server 2008数据库和Java服务层组成,我们通过存储过程和Spring SimpleJdbcCall访问所有数据。

提到的最佳做法之一是使用Joda Time库。由于这对我来说是新的,因为datetimeoffset SQL数据类型,我想确保我正确地执行此操作(因此不会丢失任何信息。)

在SQL Server内部,我创建了一个表,用于测试所有各种SQL Server get-time-type函数:

CREATE TABLE MIKE_TEMP (
    ID INT NOT NULL IDENTITY,
    BLAH NVARCHAR(255),

    DT_GET_DATE DATETIME DEFAULT GETDATE() NOT NULL,
    DT_GET_UTC_DATE DATETIME DEFAULT GETUTCDATE() NOT NULL,
    DT_SYS_DATE_TIME DATETIME DEFAULT sysdatetime() NOT NULL,
    DT_SYS_UTC_DATE_TIME DATETIME DEFAULT sysutcdatetime() NOT NULL,
    DT_SYS_DATE_TIME_OFFSET DATETIME DEFAULT sysdatetimeoffset() NOT NULL,

    DTO_GET_DATE DATETIMEOFFSET DEFAULT GETDATE() NOT NULL,
    DTO_GET_UTC_DATE DATETIMEOFFSET DEFAULT GETUTCDATE() NOT NULL,
    DTO_SYS_DATE_TIME DATETIMEOFFSET DEFAULT sysdatetime() NOT NULL,
    DTO_SYS_UTC_DATE_TIME DATETIMEOFFSET DEFAULT sysutcdatetime() NOT NULL,
    DTO_SYS_DATE_TIME_OFFSET DATETIMEOFFSET DEFAULT sysdatetimeoffset() NOT NULL
);

在此表中,我添加了一个值blah = 'Hello World!'。结果数据是:

ID BLAH         DT_GET_DATE         DT_GET_UTC_DATE     DT_SYS_DATE_TIME    DT_SYS_UTC_DATE_TIME DT_SYS_DATE_TIME_OFFSET DTO_GET_DATE                       DTO_GET_UTC_DATE                   DTO_SYS_DATE_TIME                  DTO_SYS_UTC_DATE_TIME              DTO_SYS_DATE_TIME_OFFSET           
-- ------------ ------------------- ------------------- ------------------- -------------------- ----------------------- ---------------------------------- ---------------------------------- ---------------------------------- ---------------------------------- ---------------------------------- 
1  Hello World! 2012-02-15 08:58:41 2012-02-15 14:58:41 2012-02-15 08:58:41 2012-02-15 14:58:41  2012-02-15 08:58:41     2012-02-15 08:58:41.6000000 +00:00 2012-02-15 14:58:41.6000000 +00:00 2012-02-15 08:58:41.6005458 +00:00 2012-02-15 14:58:41.6005458 +00:00 2012-02-15 08:58:41.6005458 -06:00 

有一个相应的存储过程只执行select * from MIKE_TEMP并返回所有数据作为输出参数。

访问此数据的Java代码(为清晰起见,仅包含“有趣的”导入):

import org.joda.time.DateTime;
import java.util.Date;

@Component
public class MikeTempDaoImpl {
    private static final Logger logger = LoggerFactory.getLogger(MikeTempDaoImpl.class);

    private DataSource dataSource;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public MikeVTemp getMikeTemp() {
        SimpleJdbcCall data = new SimpleJdbcCall(getDataSource());

        data.withProcedureName("get_MIKE_TEMP");
        data.withoutProcedureColumnMetaDataAccess();
        data.declareParameters(
                new SqlOutParameter("ID", Types.INTEGER),
                new SqlOutParameter("BLAH", Types.NVARCHAR),
                new SqlOutParameter("DT_GET_DATE", Types.TIMESTAMP),
                new SqlOutParameter("DT_GET_UTC_DATE", Types.TIMESTAMP),
                new SqlOutParameter("DT_SYS_DATE_TIME", Types.TIMESTAMP),
                new SqlOutParameter("DT_SYS_UTC_DATE_TIME", Types.TIMESTAMP),
                new SqlOutParameter("DT_SYS_DATE_TIME_OFFSET", Types.TIMESTAMP),
                new SqlOutParameter("DTO_GET_DATE", Types.TIMESTAMP),
                new SqlOutParameter("DTO_GET_UTC_DATE", Types.TIMESTAMP),
                new SqlOutParameter("DTO_SYS_DATE_TIME", Types.TIMESTAMP),
                new SqlOutParameter("DTO_SYS_UTC_DATE_TIME", Types.TIMESTAMP),
                new SqlOutParameter("DTO_SYS_DATE_TIME_OFFSET", Types.TIMESTAMP)
        );

        Map out;

        try {
            out = data.execute();
        } catch (Exception ex) {
            logger.error(ex.getMessage());
        }

        int id = (Integer) out.get("ID");
        String blah = (String) out.get("BLAH");
        DateTime dtGetDate = new DateTime((Date) out.get("DT_GET_DATE"));
        DateTime dtGetUtcDate = new DateTime((Date) out.get("DT_GET_UTC_DATE"));
        DateTime dtSysDateTime = new DateTime((Date) out.get("DT_SYS_DATE_TIME"));
        DateTime dtSysUtcDateTime = new DateTime((Date) out.get("DT_SYS_UTC_DATE_TIME"));
        DateTime dtSysDateTimeOffset = new DateTime((Date) out.get("DT_SYS_DATE_TIME_OFFSET"));
        DateTime dtoGetDate = new DateTime((Date) out.get("DTO_GET_DATE"));
        DateTime dtoGetUtcDate = new DateTime((Date) out.get("DTO_GET_UTC_DATE"));
        DateTime dtoSysDateTime = new DateTime((Date) out.get("DTO_SYS_DATE_TIME"));
        DateTime dtoSysUtcDateTime = new DateTime((Date) out.get("DTO_SYS_UTC_DATE_TIME"));
        DateTime dtoSysDateTimeOffset = new DateTime((Date) out.get("DTO_SYS_DATE_TIME_OFFSET"));

        MikeTemp mt = new MikeTemp.Builder()
                .id(id)
                .blah(blah)
                .dtGetDate(dtGetDate)
                .dtGetUtcDate(dtGetUtcDate)
                .dtSysDateTime(dtSysDateTime)
                .dtSysUtcDateTime(dtSysUtcDateTime)
                .dtSysDateTimeOffset(dtSysDateTimeOffset)
                .dtoGetDate(dtoGetDate)
                .dtoGetUtcDate(dtoGetUtcDate)
                .dtoSysDateTime(dtoSysDateTime)
                .dtoSysUtcDateTime(dtoSysUtcDateTime)
                .dtoSysDateTimeOffset(dtoSysDateTimeOffset)
                .build();

        System.out.println("id                   = [" + mt.getId() + "]");
        System.out.println("blah                 = [" + mt.getBlah() + "]");
        System.out.println("dtGetDate            = [" + mt.getDtGetDate() + "]");
        System.out.println("dtGetUtcDate         = [" + mt.getDtGetUtcDate() + "]");
        System.out.println("dtSysDateTime        = [" + mt.getDtSysDateTime() + "]");
        System.out.println("dtSysUtcDateTime     = [" + mt.getDtSysUtcDateTime() + "]");
        System.out.println("dtSysDateTimeOffset  = [" + mt.getDtSysDateTimeOffset() + "]");
        System.out.println("dtoGetDate           = [" + mt.getDtoGetDate() + "]");
        System.out.println("dtoGetUtcDate        = [" + mt.getDtoGetUtcDate() + "]");
        System.out.println("dtoSysDateTime       = [" + mt.getDtoSysDateTime() + "]");
        System.out.println("dtoSysUtcDateTime    = [" + mt.getDtoSysUtcDateTime() + "]");
        System.out.println("dtoSysDateTimeOffset = [" + mt.getDtoSysDateTimeOffset() + "]");

        return mvt;
    }
}

这是通过JUnit测试来实现的:

@Test
public void testDateData() throws Exception {
    MikeTemp mt = dao.getMikeTemp();

    assertNotNull("MT should not be null, but it is.", mt);
    assertEquals(1, mt.getId());
    assertEquals("Hello World!", mt.getBlah());
}

所有println的结果都是:

id                   = [1]
blah                 = [Hello World!]
dtGetDate            = [2012-02-15T08:58:41.577-06:00]
dtGetUtcDate         = [2012-02-15T14:58:41.577-06:00]
dtSysDateTime        = [2012-02-15T08:58:41.580-06:00]
dtSysUtcDateTime     = [2012-02-15T14:58:41.600-06:00]
dtSysDateTimeOffset  = [2012-02-15T08:58:41.600-06:00]
dtoGetDate           = [2012-02-15T08:58:41.600-06:00]
dtoGetUtcDate        = [2012-02-15T14:58:41.600-06:00]
dtoSysDateTime       = [2012-02-15T08:58:41.600-06:00]
dtoSysUtcDateTime    = [2012-02-15T14:58:41.600-06:00]
dtoSysDateTimeOffset = [2012-02-15T08:58:41.600-06:00]

由于此服务器位于美国中部时区,我绝对希望看到一些结果的-06:00,但绝对不是所有他们。我在路上的某个地方错过了什么吗?在这种情况下,使用DateTime(Object)对象调用Joda java.util.Date ctor是否正确?还有什么可以/我应该做什么,我不是?

谢谢!

2 个答案:

答案 0 :(得分:2)

不,这不是这样做的方法。您正在使用DateTime(Object)构造函数,其中Date 知道即时,并使用系统默认时区。

正如BalusC所写,如果您想自己指定时区,可以Calendar传递给ResultSet.getTimestamp() - 但这与保留已经存在的信息不同存在于数据库中。

目前尚不清楚您正在使用哪个JDBC驱动程序,并且您可能不得不离开SimpleJdbcCall,但Microsoft JDBC驱动程序v3.0具有SQLServerCallableStatement.getDateTimeOffset(同上{{} 1}})这可能是正确的做法。鉴于您的数据类型不是真正可移植的,这意味着您的代码实际上不是:(

你肯定需要保留偏移吗?如果没有,你可能会集中精力使用“普通”JDBC调用来获得正确的瞬间 - 不幸的是,它现在看起来并没有这样做。

答案 1 :(得分:1)

如果你想完全避免java.util.Date/Calendar,那就是我所做的

SQL服务器端:

SELECT CONVERT(NVARCHAR, DateFieldName, 126) AS DateFieldIsoString

其中DateFieldNamedatetimeoffset字段,查询返回java.lang.String

String解析为Joda DateTime

DateTime datetime = ISODateTimeFormat.dateTimeParser().withOffsetParsed()
          .parse(dateFieldAsString)