From 956747d8cdd83cc91a0459af7ca0006a75db36a7 Mon Sep 17 00:00:00 2001 From: Jan-Willem Gmelig Meyling Date: Wed, 11 Sep 2019 14:11:28 +0200 Subject: [PATCH] Add YearMonthTimestampType #127 In some legacy schemas it might be required to map a YearMonth to a Timestamp value, rather than the Date value that we currently support. This may for example be chosen for easy range comparisions to other timestamp values without any casting being required. This change adds such a YearMonthTimestampType. While I think YearMonthDateType will generally be the preferred type, this at least should smoothen the transition from a YearMonth modelled as a timestamp to a YearMonth value. --- changelog.txt | 2 + .../type/basic/YearMonthTimestampType.java | 45 +++++++ .../internal/YearMonthTypeDescriptor.java | 4 + .../PostgreSQLYearMonthTimestampTest.java | 116 ++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/basic/YearMonthTimestampType.java create mode 100644 hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/basic/PostgreSQLYearMonthTimestampTest.java diff --git a/changelog.txt b/changelog.txt index a089eafc6..ff4abe26c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,8 @@ Version 2.7.0 - IN PROGRESS Add support for JSON column values for Oracle #131 +Add YearMonthTimestampType #127 + Ability to use PostgreSQLEnumType and EnumArrayType with TypedParameterValue #125 Version 2.6.1 - August 29, 2019 diff --git a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/basic/YearMonthTimestampType.java b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/basic/YearMonthTimestampType.java new file mode 100644 index 000000000..01e082f1b --- /dev/null +++ b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/basic/YearMonthTimestampType.java @@ -0,0 +1,45 @@ +package com.vladmihalcea.hibernate.type.basic; + +import com.vladmihalcea.hibernate.type.AbstractHibernateType; +import com.vladmihalcea.hibernate.type.basic.internal.YearMonthTypeDescriptor; +import com.vladmihalcea.hibernate.type.util.Configuration; +import org.hibernate.type.descriptor.sql.DateTypeDescriptor; +import org.hibernate.type.descriptor.sql.TimestampTypeDescriptor; + +import java.time.YearMonth; + +/** + * Maps a Java {@link YearMonth} object to a {@code TIMESTAMP} column type. + *

+ * + * @author Vlad Mihalcea + */ +public class YearMonthTimestampType + extends AbstractHibernateType { + + public static final YearMonthTimestampType INSTANCE = new YearMonthTimestampType(); + + public YearMonthTimestampType() { + super( + TimestampTypeDescriptor.INSTANCE, + YearMonthTypeDescriptor.INSTANCE + ); + } + + public YearMonthTimestampType(Configuration configuration) { + super( + TimestampTypeDescriptor.INSTANCE, + YearMonthTypeDescriptor.INSTANCE, + configuration + ); + } + + public String getName() { + return "yearmonth-timestamp"; + } + + @Override + protected boolean registerUnderJavaType() { + return true; + } +} \ No newline at end of file diff --git a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/basic/internal/YearMonthTypeDescriptor.java b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/basic/internal/YearMonthTypeDescriptor.java index 750aff744..a00ce3d93 100644 --- a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/basic/internal/YearMonthTypeDescriptor.java +++ b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/basic/internal/YearMonthTypeDescriptor.java @@ -3,6 +3,7 @@ import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.AbstractTypeDescriptor; +import java.sql.Timestamp; import java.time.Instant; import java.time.YearMonth; import java.time.ZoneId; @@ -49,6 +50,9 @@ public X unwrap(YearMonth value, Class type, WrapperOptions options) { Integer numericValue = (value.getYear() * 100) + value.getMonth().getValue(); return (X) (numericValue); } + if (Timestamp.class.isAssignableFrom(type)) { + return (X) java.sql.Timestamp.valueOf(value.atDay(1).atStartOfDay()); + } if (Date.class.isAssignableFrom(type)) { return (X) java.sql.Date.valueOf(value.atDay(1)); } diff --git a/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/basic/PostgreSQLYearMonthTimestampTest.java b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/basic/PostgreSQLYearMonthTimestampTest.java new file mode 100644 index 000000000..47460f0a7 --- /dev/null +++ b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/basic/PostgreSQLYearMonthTimestampTest.java @@ -0,0 +1,116 @@ +package com.vladmihalcea.hibernate.type.basic; + +import com.vladmihalcea.hibernate.type.util.AbstractPostgreSQLIntegrationTest; +import org.hibernate.Session; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.TypeDef; +import org.junit.Test; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import java.time.YearMonth; + +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +public class PostgreSQLYearMonthTimestampTest extends AbstractPostgreSQLIntegrationTest { + + @Override + protected Class[] entities() { + return new Class[]{ + Book.class + }; + } + + @Test + public void test() { + doInJPA(entityManager -> { + Book book = new Book(); + book.setIsbn("978-9730228236"); + book.setTitle("High-Performance Java Persistence"); + book.setPublishedOn(YearMonth.of(2016, 10)); + + entityManager.persist(book); + }); + + doInJPA(entityManager -> { + Book book = entityManager + .unwrap(Session.class) + .bySimpleNaturalId(Book.class) + .load("978-9730228236"); + + assertEquals(YearMonth.of(2016, 10), book.getPublishedOn()); + }); + + doInJPA(entityManager -> { + Book book = entityManager + .createQuery( + "select b " + + "from Book b " + + "where " + + " b.title = :title and " + + " b.publishedOn = :publishedOn", Book.class) + .setParameter("title", "High-Performance Java Persistence") + .setParameter("publishedOn", YearMonth.of(2016, 10)) + .getSingleResult(); + + assertEquals("978-9730228236", book.getIsbn()); + }); + } + + + @Entity(name = "Book") + @Table(name = "book") + @TypeDef(typeClass = YearMonthTimestampType.class, defaultForType = YearMonth.class) + public static class Book { + + @Id + @GeneratedValue + private Long id; + + @NaturalId + private String isbn; + + private String title; + + @Column(name = "published_on", columnDefinition = "date") + private YearMonth publishedOn; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getIsbn() { + return isbn; + } + + public void setIsbn(String isbn) { + this.isbn = isbn; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public YearMonth getPublishedOn() { + return publishedOn; + } + + public void setPublishedOn(YearMonth publishedOn) { + this.publishedOn = publishedOn; + } + } +}