Сопоставьте столбец метки времени базы данных с календарем UTC (JPA) и передайте его как дату UTC через веб-сервис (jax-ws)

StackOverflow https://stackoverflow.com/questions/1821104

  •  10-07-2019
  •  | 
  •  

Вопрос

Это звучит как простая задача.
Получите значение метки времени UTC из базы данных и передайте его как дату UTC через веб-сервис.

У нас есть столбец временной метки DATE_COLUMN и мы сохраняем там время в часовом поясе UTC.

С JPA мы получаем на этот раз с

@Column(name = "DATE_COLUMN")
private java.sql.Timestamp dateValue;

И поскольку мы должны передать это время через веб-сервис в UTC (Jax-ws 2.0), у нас есть методы getDate и setDate.
Нас интересует getDate.

public Calendar getDate()
{
   Calendar calendar = Calendar.getInstance(utcTimeZone);
   calendar.setTimeInMillis(dateValue.getTime());

   return calendar;
}

Это работает не так, как вы, возможно, думаете, должно.
И это потому, что часовой пояс приложения по умолчанию не является "UTC".

Вот пример для пояснения.
Значение в DATE_COLUMN равно "30.11.09 16:34: 48,833045000", когда я перевожу его в UTC, я получаю "2009-11-30T14:34:48.833Z".
Разница составляет 2 часа.И это потому, что мой часовой пояс по умолчанию - "Европа / Хельсинки".

Та же проблема, если вы просто хотите сопоставить 'DATE_COLUMN' с Calendar

@Column(name = "DATE_COLUMN")
@Temporal(TemporalType.TIMESTAMP)
private Calendar dateValue;

public Calendar getDate()
{
   calendar.setTimeZone(utcTimeZone);
   return calendar;
}

Я не хочу менять часовой пояс приложения, потому что это не похоже на решение.

На данный момент у нас есть только два варианта.

Первый.Вычислите смещение между часовым поясом приложения и UTC и добавьте его вручную после автоматического вычитания в calendar.setTimeZone.

public Calendar getDate()
{
   Calendar calendar = Calendar.getInstance(utcTimeZone);
   calendar.setTimeInMillis(dateValue.getTime());

   int offset = TimeZone.getDefault().getOffset(dateValue.getTime());

   calendar.add(Calendar.MILLISECOND, offset);

   return calendar;
}

Второй.Передайте dateValue как Long через веб-сервис.Что неплохо, за исключением того, что мы теряем реальный тип поля в wsdl.

Мое воображаемое решение таково

@Column(name = "DATE_COLUMN")
@Temporal(type = TemporalType.TIMESTAMP, timezone = 'UTC')
private Calendar dateValue;

Но я склонен думать, что где-то есть настоящий.И я надеюсь, что вы сможете указать на это.

Это было полезно?

Решение

Мы решили использовать следующее решение.
Использование Date для получения даты из базы данных.Это потому, что Date является типом без временных зон.

@Column(name = "DATE_COLUMN")
@Temporal(TemporalType.TIMESTAMP)
private Date dateValue;

public Date getDate()
{
   return dateValue;
}

И чтобы отправить его через веб-сервис в UTC (jax-ws), мы создали UtcTimestampAdapter чтобы изменить зону приложения по умолчанию на UTC на этапе маршалинга.

public class UtcTimestampAdapter extends XmlAdapter<XMLGregorianCalendar, Date>
{
   @Override
   public XMLGregorianCalendar marshal(Date date) throws Exception
   {
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTime(date);

      DatatypeFactory dataTypeFactory = DatatypeFactory.newInstance();
      XMLGregorianCalendar xmlCalendar = 
         dataTypeFactory.newXMLGregorianCalendar(calendar);

      //Reset time zone to UTC
      xmlCalendar.setTimezone(0);

      return xmlCalendar;
   }

   @Override
   public Date unmarshal(XMLGregorianCalendar calendar) throws Exception
   {
      return calendar.toGregorianCalendar().getTime();
   }
}

Затем включить это правило для всех Dataдля полей в модуле мы добавили настройку, специфичную для конкретного пакета, вот так.

@XmlJavaTypeAdapter(value = UtcTimestampAdapter.class, type = Date.class)
@XmlSchemaType(name = "dateTime", type = XMLGregorianCalendar.class)
package com.companyname.modulename;

Вот и все.Теперь у нас есть универсальное решение, которое инкапсулирует всю логику в одном месте.И если мы хотим отправлять даты без часового пояса в формате UTC через веб-сервис в каком-либо другом модуле, мы просто пометим определенный пакет.

Другие советы

Если вам нужно, чтобы процесс Java выполнялся в часовом поясе UTC, самый простой способ сделать это - добавить следующий параметр JVM:

-Duser.timezone=UTC

TimeZone.setDefault (TimeZone.getTimeZone (" UTC ")); влияет на всю JVM.

Это приведет к сбою других приложений, если они ожидают местное время

Другое решение - установить часовой пояс по умолчанию для приложения только в @StartupBean :

import java.util.TimeZone;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;

@Startup
@Singleton
public class StartupBean {

    @PostConstruct
    public void initializeTheServer() {
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
    }
}

С этого момента вся интерпретация объектов Date будет основываться на UTC. Это включает в себя сортировку XML.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top