Mapa coluna timestamp banco de dados para UTC Calendar (JPA) e passá-lo como data UTC via WebService (JAX-WS)

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

  •  10-07-2019
  •  | 
  •  

Pergunta

Isso soa como uma tarefa simples.
Obter valor timestamp UTC de DB e passá-lo como data UTC via Web Service.

Temos timestamp coluna_data coluna e armazene há tempo no fuso horário UTC.

Com JPA temos desta vez com

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

E como nós temos que passar desta vez via Web Service em UTC (JAX-WS 2.0), temos getDate e métodos SetDate.
Estamos interessados ??em getDate.

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

   return calendar;
}

Isto não funciona como você pode pensar que deveria.
E isso é porque fuso horário padrão do aplicativo não é 'UTC'.

Aqui está um exemplo de esclarecimento.
Valor no coluna_data igual a "30.11.09 16: 34: 48,833045000", quando eu traduzi-lo para UTC eu chegar. "2009-11-30T14: 34: 48.833Z"
A diferença é de 2 horas. E isso é porque o meu fuso horário padrão é "Europa / Helsínquia".

Mesmo problema se você quiser apenas para mapear 'coluna_data' para Calendar

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

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

Eu não quero o fuso horário da aplicação de mudança porque ele não se parece com a solução.

Até agora temos apenas duas opções.

Primeiro . Calcule deslocamento entre o fuso horário da aplicação e UTC e adicioná-lo manualmente após a subtração automática no 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;
}

Segundo . Passe DATEVALUE como Long via Web Service. O que não é ruim, exceto que perdemos tipo real do campo no WSDL.

A minha solução imaginária é

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

Mas eu tendem a pensar que não é aquele em algum lugar real. E eu espero que você pode indicá-lo.

Foi útil?

Solução

Nós decidimos usar seguinte solução.
Use Date para recuperar data a partir da base de dados. É porque Date é tipo timezoneless.

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

public Date getDate()
{
   return dateValue;
}

E para enviá-lo via WebService em UTC (JAX-WS) criamos UtcTimestampAdapter a zona de mudança do padrão do aplicativo para UTC na fase de empacotamento.

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();
   }
}

Em seguida, para activar esta regra a todos os Datas campos no módulo nós adicionamos definição pacote específico como tal.

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

É isso. Agora temos solução genérica que encapsular toda a lógica em um só lugar. E se queremos enviar datas timezoneless como UTC através do serviço web em algum outro módulo iremos certo pacote apenas anotar.

Outras dicas

Se você precisa o processo java a correr no fuso horário UTC, a maneira mais fácil de fazer isso é adicionando o seguinte parâmetro JVM:

-Duser.timezone=UTC

TimeZone.setDefault(TimeZone.getTimeZone("UTC")); parece estar afetando toda a JVM.

Isto fará com que outras aplicações falhar se eles estavam esperando, hora local

Outra solução é definir o fuso horário padrão para o aplicativo somente em um @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"));
    }
}

A partir de então, toda interpretação de objetos Data será baseado no UTC. Isto inclui triagem XML.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top