Лучший способ получить данные даты SQL Server в объект Joda? - PullRequest
2 голосов
/ 18 февраля 2012

TL; DR версия: Пожалуйста, подтвердите (или, если нет, предоставьте помощь), что я правильно получаю данные SQL Server datetimeoffset в мои объекты Java Joda Time.


Я нахожусь вСередина планирования - перевести нашу базу данных и код Java в режим часовых поясов.Чтобы достичь этого, я уже прочитал этот пост и пытаюсь применить лучшие практики.Обратите внимание, что весь код считается «выбрасываемым» кодом, поэтому я не особо обеспокоен его эффективностью;просто правильность.

Наша среда состоит из базы данных Microsoft SQL Server 2008 и уровня обслуживания Java, благодаря которому мы получаем доступ ко всем данным через хранимые процедуры и Spring SimpleJdbcCall.

Одна из лучшихУпоминается практика использования библиотеки Joda Time.Поскольку для меня это ново, как и тип данных datetimeoffset SQL, я хотел бы убедиться, что я делаю это правильно (и, таким образом, не теряю никакой информации.)

Внутри SQL Server я создалтаблица для тестирования всех различных функций типа времени получения SQL Server:

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 для некоторых результатов, но определенно не всех из них.Я что-то пропустил где-то по пути?Является ли вызов Joda DateTime(Object) ctor с объектом java.util.Date правильным решением в этой ситуации?Что еще я мог / должен делать, что я не такой?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 23 февраля 2012

Нет, это не способ сделать это. Вы используете конструктор DateTime(Object) с Date, который только знает о моменте времени и использует системный часовой пояс по умолчанию.

Как писал BalusC, вы могли бы передать Calendar в ResultSet.getTimestamp(), если хотите указать часовой пояс самостоятельно, но это не то же самое, что сохранить информацию, которая уже присутствует в базе данных.

Не ясно, какой драйвер JDBC вы используете, и вам, вероятно, придется отойти от SimpleJdbcCall, но драйвер Microsoft JDBC v3.0 имеет SQLServerCallableStatement.getDateTimeOffset (и то же самое для SQLServerResultSet) что, вероятно, будет делать правильные вещи. Учитывая, что ваш тип данных не является действительно переносимым, это означает, что ваш код не может быть либо: (

Вам определенно нужно сохранить смещение? Если нет, вы, вероятно, могли бы сконцентрироваться на том, чтобы получить правильное мгновение, используя «нормальные» вызовы JDBC - что, к сожалению, похоже, что в данный момент не выполняется.

1 голос
/ 13 августа 2012

Если вы хотите полностью избежать java.util.Date/Calendar, это то, что я сделал

на стороне сервера SQL:

SELECT CONVERT(NVARCHAR, DateFieldName, 126) AS DateFieldIsoString

, где DateFieldName - это поле datetimeoffset,и запрос возвращает java.lang.String

парсинг String в Joda DateTime:

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