2012-02-17 71 views
2

TL; DR版本:請確認(或者如果沒有,請提供幫助),我正確地將SQL Server datetimeoffset數據正確加載到我的Joda Time Java對象中。將SQL Server日期數據導入Joda對象的最佳方法是什麼?


我正在計劃中,將我們的數據庫和Java代碼移到時區感知的位置。爲了做到這一點,我一直都在this post,並試圖實施最佳做法。請注意,所有代碼都被認爲是「丟棄」代碼,所以我並不在乎效率。只是正確。

我們的環境由Microsoft SQL Server 2008數據庫和Java服務層組成,我們通過存儲過程和Spring訪問所有數據。

提到的最佳實踐之一是使用喬達時間庫。由於這對我來說是新的,所以我想確保我正確地做到這一點(並且因此不會丟失任何信息)。

在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] 

作爲此服務器是在美國中部時區,我defi翹首以盼-06:00爲部分的結果,但絕對不是全部是。我錯過了沿途的某個地方嗎?打電話給喬達DateTime(Object) ctor與java.util.Date對象在這種情況下做正確的事情?我還能做什麼?我不是?

謝謝!

+1

你的關鍵的問題是,'java.util.Date'不存儲自定義時區信息。它始終是UTC。爲了給出一個合適的答案,這段代碼從原始JDBC中抽象得太多了,但是您可能需要使用'ResultSet#getTimestamp()'方法來傳遞包含時區的'java.util.Calendar'。 – BalusC 2012-02-20 03:02:44

回答

2

不,這不是做這件事的方法。您正在使用DateTime(Object)構造函數與Date哪個只有知道一個即時的時間,並使用系統默認時區。

由於BalusC寫道,你可以通過一個CalendarResultSet.getTimestamp()如果要指定自己的時區 - 但這是不一樣的保護這是已經存在於數據庫中的信息。

目前尚不清楚你使用的JDBC驅動程序,你可能不得不從SimpleJdbcCall移開,但微軟的JDBC驅動程序V3.0具有SQLServerCallableStatement.getDateTimeOffset(同上,併爲SQLServerResultSet),這大概會做正確的事。鑑於你的數據類型不是真正的便攜式,這意味着你的代碼不能真的是:(

你是否一定需要保留偏移量?如果不是,你可以專注於使用正確的瞬間。「正常」的JDBC調用 - 不幸的是,它看起來像它不這樣做,此刻

+0

感謝指向MSJDBC 3.0驅動程序的指針。我們目前使用的驅動程序是2.0,但似乎更新版本將使我們能夠在本地支持Java代碼中的datetimeoffset數據類型,而不會丟失數據庫端的信息。合理地確定這非常接近我們正在尋找的東西。再次感謝! – Mike 2012-02-24 16:15:17

1

如果你想避免java.util.Date/Calendar完全,這是我做了什麼

SQL服務器端:

SELECT CONVERT(NVARCHAR, DateFieldName, 126) AS DateFieldIsoString 

其中DateFieldName是一個datetimeoffset字段,查詢返回java.lang.String

解析String成喬達DateTime

DateTime datetime = ISODateTimeFormat.dateTimeParser().withOffsetParsed() 
      .parse(dateFieldAsString) 
相關問題