J'ai une application SpringBoot. en utilisant Spring Initializer, Tomcat intégré, le moteur de modèle Thymeleaf et le package en tant que fichier JAR exécutable.
J'ai un objet de domaine avec 2 propriétés (initDate, endDate). Je veux créer 2 convertisseurs pour gérer la base de données mySQL
@Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime initDate;
@Convert(converter = ZonedDateTimeAttributeConverter.class)
private ZonedDateTime endDate;
le convertisseur 1 (est OK)
@Converter
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(LocalDateTime localDateTime) {
return (localDateTime == null ? null : Timestamp.valueOf(localDateTime));
}
@Override
public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
}
}
C'est celui que je veux créer
@Converter
public class ZonedDateTimeAttributeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime zoneDateTime) {
return (zoneDateTime == null ? null : Timestamp.valueOf(zoneDateTime));
}
@Override
public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toZonedDateTime());
}
}
Mais je ne peux pas car j'ai 2 erreurs:
The method valueOf(String) in the type Timestamp is not applicable for the arguments (ZonedDateTime)
et le TimeStamp n'a pas la méthode toZonedDateTime()
et si je n'ajoute aucun convertisseur pour le ZonedDate, JPA crée une table avec le type varbinary(255)
Timestamp étend Date
pour fournir une précision en nanosecondes. Ni Date
ni Timestamp
ne sont conçus pour faire référence à un fuseau horaire spécifique en tant que ZoneDateTime
.
Si vous devez convertir ZonedDateTime
-> Timestamp
, vous devrez supprimer les informations de fuseau horaire/décalage. Par exemple.
LocalDateTime withoutTimezone = zoneDateTime.toLocalDateTime();
Timestamp timestamp = Timestamp.valueOf(withoutTimezone));
et pour convertir Timestamp
-> ZonedDateTime
vous devez spécifier un décalage:
LocalDateTime withoutTimezone = sqlTimestamp.toLocalDateTime();
ZonedDateTime withTimezone = withoutTimezone.atZone(ZoneId.of("+03:00"));
ou fuseau horaire:
ZonedDateTime withTimezone = withoutTimezone.atZone(ZoneId.of("Europe/Paris"));
Si votre intention est de sauvegarder ZonedDateTime
variables dans la base de données et de conserver les différents fuseaux horaires qui y sont spécifiés, je recommande de concevoir votre base de données en conséquence. Suggestions:
DATETIME
pour enregistrer un LocalDateTime
et un VARCHAR
enregistrement d'un fuseau horaire comme "Europe/Paris"
ou SMALLINT
sauvegarde d'un décalage en minutes.ZonedDateTime
en String
et enregistrez-le dans une colonne VARCHAR
comme "2017-05-16T14:12:48.983682+01:00[Europe/London]"
. Vous devrez ensuite l'analyser lors de la lecture de la base de données.Jon Skeet l'a déjà dit:
@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime zoneDateTime) {
return zoneDateTime == null ? null : Timestamp.from(zoneDateTime.toInstant());
}
@Override
public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return sqlTimestamp == null ? null : sqlTimestamp.toInstant().atZone(ZoneId.systemDefault());
}
Jon a également posé la bonne question, quel fuseau horaire voulez-vous? J'ai deviné ZoneId.systemDefault()
. Évidemment, un fuseau horaire différent donnera un résultat différent, donc j'espère que vous y réfléchirez à deux fois et serez en mesure de trouver le bon fuseau horaire pour votre objectif.
PS J'ai réduit l'utilisation des parenthèses depuis que je l'ai trouvé plus lisible avec moins. Vous pouvez les rajouter si vous préférez.