Mappez la colonne d'horodatage de la base de données sur le calendrier UTC (JPA) et transmettez-la en tant que date UTC via WebService (jax-ws)

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

  •  10-07-2019
  •  | 
  •  

Question

Cela semble être une tâche simple.
Obtenez la valeur d'horodatage UTC auprès de la base de données et transmettez-la en tant que date UTC via le service Web.

Nous avons la colonne d'horodatage DATE_COLUMN et nous stockons l'heure dans le fuseau horaire UTC.

Avec JPA, nous obtenons cette fois-ci avec

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

Et comme nous devons passer cette fois via le service Web au format UTC (Jax-ws 2.0), nous avons les méthodes getDate et setDate.
GetDate nous intéresse.

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

   return calendar;
}

Cela ne fonctionne pas comme vous le pensez peut-être.
Et cela est dû au fait que le fuseau horaire par défaut de l'application n'est pas «UTC».

Voici un exemple de clarification.
La valeur dans DATE_COLUMN est égale à "30.11.09 16: 34: 48,833045000" ", lorsque je le traduis en UTC, j'obtiens" 2009-11-30T14: 34: 48.833Z ".

La différence est de 2 heures. Et c’est parce que mon fuseau horaire par défaut est "Europe / Helsinki".

Même problème si vous souhaitez simplement mapper 'DATE_COLUMN' sur Agenda

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

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

Je ne souhaite pas modifier le fuseau horaire de l'application car il ne ressemble pas à la solution.

À présent, nous n’avons que deux options.

Premier . Calculez le décalage entre le fuseau horaire de l'application et l'heure UTC et ajoutez-le manuellement après soustraction automatique dans la page 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;
}

Deuxième . Transmettez dateValeur en tant que Long via le service Web. Ce qui n’est pas mal sauf que nous perdons le type réel du champ dans wsdl.

Ma solution imaginaire est

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

Mais j'ai tendance à penser qu'il y a le vrai quelque part. Et j'espère que vous pourrez le signaler.

Était-ce utile?

La solution

Nous avons décidé d'utiliser la solution suivante.
Utilisez Date pour extraire la date de la base de données. C’est parce que Date est un type sans délai.

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

public Date getDate()
{
   return dateValue;
}

Et pour l'envoyer via WebService au format UTC (jax-ws), nous avons créé UtcTimestampAdapter pour modifier la zone de la zone par défaut de l'application en UTC lors de la phase de marshaling.

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

Ensuite, pour activer cette règle sur tous les champs Données du module, nous avons ajouté un paramètre spécifique au package, comme ceci.

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

C'est ça. Nous avons maintenant une solution générique qui encapsule toute la logique au même endroit. Et si nous voulons envoyer des dates sans fuseau horaire au format UTC via un service Web dans un autre module, nous annotons simplement certains paquets.

Autres conseils

Si vous souhaitez que le processus Java s'exécute dans le fuseau horaire UTC, la méthode la plus simple consiste à ajouter le paramètre JVM suivant:

-Duser.timezone=UTC

TimeZone.setDefault (TimeZone.getTimeZone ("UTC")); semble affecter l'ensemble de la JVM.

Cela ferait échouer d'autres applications si elles attendaient l'heure locale

Une autre solution consiste à définir le fuseau horaire par défaut pour l'application uniquement dans un @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"));
    }
}

Dès lors, toute interprétation des objets Date sera basée sur UTC. Cela inclut le marshalling XML.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top