<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- used versions -->
+ <h2.version>1.4.190</h2.version>
+ <hibernate.version>5.0.2.Final</hibernate.version>
<hibernate-jpa-2.1-api.version>1.0.0.Final</hibernate-jpa-2.1-api.version>
<junit.version>4.12</junit.version>
<slf4j.version>1.7.13</slf4j.version>
</properties>
-
<dependencies>
<!-- JPA -->
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>${hibernate.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <version>${h2.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
--- /dev/null
+package de.juplo.jpa.converters;
+
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ *
+ * @author Kai Moritz
+ */
+@Entity
+@Access(AccessType.PROPERTY)
+public class EierlegendeWollmilchSau
+{
+ Logger log = LoggerFactory.getLogger(EierlegendeWollmilchSau.class);
+
+
+ Long id;
+
+ OffsetDateTime odt;
+ ZonedDateTime zdt;
+
+
+ @Id
+ @GeneratedValue
+ public Long getId()
+ {
+ return id;
+ }
+
+ public void setId(Long id)
+ {
+ this.id = id;
+ }
+
+
+ @Convert(converter = OffsetDateTimeConverter.class)
+ public OffsetDateTime getOdt()
+ {
+ log.trace("get OffsetDateTime: {}", odt);
+ return odt;
+ }
+
+ public void setOdt(OffsetDateTime odt)
+ {
+ log.trace("set OffsetDateTime: {}", odt);
+ this.odt = odt;
+ }
+
+ @Convert(converter = ZonedDateTimeConverter.class)
+ public ZonedDateTime getZdt()
+ {
+ log.trace("get ZonedDateTime: {}", zdt);
+ return zdt;
+ }
+
+ public void setZdt(ZonedDateTime zdt)
+ {
+ log.trace("set ZonedDateTime: {}", zdt);
+ this.zdt = zdt;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ boolean first = true;
+ StringBuilder builder = new StringBuilder();
+ if (odt != null)
+ {
+ builder.append("OffsetDateTime: ");
+ builder.append(odt);
+ first = false;
+ }
+ if (zdt != null)
+ {
+ if (!first)
+ builder.append(", ");
+ builder.append("ZonedDateTime: ");
+ builder.append(zdt);
+ first = false;
+ }
+ return builder.toString();
+ }
+}
--- /dev/null
+package de.juplo.jpa.converters;
+
+import de.juplo.jpa.converters.TimeConversions.FixedZoneIdStrategy;
+import de.juplo.jpa.converters.TimeConversions.FixedZoneOffsetStrategy;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.persistence.Persistence;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ *
+ * @author kai
+ */
+public class JPAConverterTest
+{
+ final static Logger log = LoggerFactory.getLogger(JPAConverterTest.class);
+
+
+ EntityManager em =
+ Persistence.createEntityManagerFactory("test").createEntityManager();
+
+ ZonedDateTime zdt = ZonedDateTime.now();
+ OffsetDateTime odt = OffsetDateTime.now();
+
+ ZoneId defaultZone = ZoneId.systemDefault();
+ ZoneId otherZone = defaultZone.getId().equals("America/New_York") ? ZoneId.of("Europe/Berlin") : ZoneId.of("America/New_York");
+
+ ZoneOffset defaultOffset = defaultZone.getRules().getOffset(odt.toLocalDateTime());
+ ZoneOffset otherOffset = ZoneOffset.ofTotalSeconds(defaultOffset.getTotalSeconds() + 3 * 60 * 60);
+
+
+ @Test
+ public void testOffsetDateTime()
+ {
+ EierlegendeWollmilchSau ews;
+ Long id;
+
+ TimeConversions.setZoneOffsetStrategy(new FixedZoneOffsetStrategy(defaultOffset));
+ ews = new EierlegendeWollmilchSau();
+ ews.odt = odt;
+ id = persist(ews);
+ ews = retrieve(id);
+ assertEquals(odt, ews.odt);
+
+ TimeConversions.setZoneOffsetStrategy(new FixedZoneOffsetStrategy(otherOffset));
+ ews = new EierlegendeWollmilchSau();
+ ews.odt = odt;
+ id = persist(ews);
+ ews = retrieve(id);
+ assertEquals(odt.toInstant(), ews.odt.toInstant());
+ }
+
+ @Test
+ public void testZonedDateTime()
+ {
+ EierlegendeWollmilchSau ews;
+ Long id;
+
+ TimeConversions.setZoneIdStrategy(new FixedZoneIdStrategy(defaultZone));
+ ews = new EierlegendeWollmilchSau();
+ ews.zdt = zdt;
+ id = persist(ews);
+ ews = retrieve(id);
+ assertEquals(zdt, ews.zdt);
+
+ TimeConversions.setZoneIdStrategy(new FixedZoneIdStrategy(otherZone));
+ ews = new EierlegendeWollmilchSau();
+ ews.zdt = zdt;
+ id = persist(ews);
+ ews = retrieve(id);
+ assertEquals(zdt.toInstant(), ews.zdt.toInstant());
+ }
+
+
+ private Long persist(EierlegendeWollmilchSau ews)
+ {
+ EntityTransaction tx = em.getTransaction();
+ tx.begin();
+ em.persist(ews);
+ log.info("persisting {}", ews);
+ tx.commit();
+ em.clear();
+ return ews.id;
+ }
+
+ private EierlegendeWollmilchSau retrieve(Long id)
+ {
+ EierlegendeWollmilchSau ews;
+ EntityTransaction tx = em.getTransaction();
+ tx.begin();
+ ews = em.find(EierlegendeWollmilchSau.class, id);
+ tx.rollback();
+ log.info("retrieved {}", ews);
+ return ews;
+ }
+}
--- /dev/null
+package de.juplo.jpa.converters;
+
+import de.juplo.jpa.converters.TimeConversions.FixedZoneOffsetStrategy;
+import java.sql.Timestamp;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ *
+ * @author Kai Moritz
+ */
+public class OffsetDateTimeConverterTest
+{
+ Logger log = LoggerFactory.getLogger(OffsetDateTimeConverterTest.class);
+
+ OffsetDateTimeConverter converter = new OffsetDateTimeConverter();
+ OffsetDateTime odt = OffsetDateTime.now();
+ ZoneOffset defaultOffset = ZoneId.systemDefault().getRules().getOffset(odt.toLocalDateTime());
+ ZoneOffset otherOffset = ZoneOffset.ofTotalSeconds(defaultOffset.getTotalSeconds() + 3 * 60 * 60);
+
+
+ @Test
+ public void testConvertToDatabaseColumn()
+ {
+ Timestamp ts;
+
+ TimeConversions.setZoneOffsetStrategy(new FixedZoneOffsetStrategy(defaultOffset));
+ ts = converter.convertToDatabaseColumn(odt);
+ log.info("{}: {} -> {}", defaultOffset, odt, ts);
+ assertEquals(odt.toInstant(), ts.toInstant());
+
+ TimeConversions.setZoneOffsetStrategy(new FixedZoneOffsetStrategy(otherOffset));
+ ts = converter.convertToDatabaseColumn(odt);
+ log.info("{}: {} -> {}", otherOffset, odt, ts);
+ assertEquals(odt.toInstant(), OffsetDateTime.of(ts.toLocalDateTime(), otherOffset).toInstant());
+ }
+
+ @Test
+ public void testRoundtrip()
+ {
+ Timestamp ts;
+
+ TimeConversions.setZoneOffsetStrategy(new FixedZoneOffsetStrategy(defaultOffset));
+ ts = converter.convertToDatabaseColumn(odt);
+ assertEquals(odt.toInstant(), converter.convertToEntityAttribute(ts).toInstant());
+
+ TimeConversions.setZoneOffsetStrategy(new FixedZoneOffsetStrategy(otherOffset));
+ ts = converter.convertToDatabaseColumn(odt);
+ assertEquals(odt.toInstant(), converter.convertToEntityAttribute(ts).toInstant());
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence
+ xmlns="http://java.sun.com/xml/ns/persistence"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ version="1.0">
+
+ <persistence-unit name="test">
+ <class>de.juplo.jpa.converters.OffsetDateTime</class>
+ <class>de.juplo.jpa.converters.ZonedDateTime</class>
+ <properties>
+ <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
+ <property name="javax.persistence.jdbc.user" value="sa"/>
+ <property name="javax.persistence.jdbc.password" value=""/>
+ <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:"/>
+ <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
+ <property name="hibernate.hbm2ddl.auto" value="create"/>
+ </properties>
+ </persistence-unit>
+
+</persistence>
--- /dev/null
+log4j.rootLogger=INFO,stdout
+
+log4j.logger.de.juplo=TRACE
+
+log4j.logger.org.hibernate=WARN
+log4j.logger.org.hibernate.SQL=DEBUG
+log4j.logger.org.hibernate.type=TRACE
+log4j.logger.org.hibernate.type.BasicTypeRegistry=ERROR
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c:%L - %m%n