Skip to content

Commit b37c4a7

Browse files
mladensavic94vladmihalcea
authored andcommitted
Add MonthDayType to map a java.time.MonthDay to either a DATE or an INTEGER column type vladmihalcea#262
1 parent 5958160 commit b37c4a7

File tree

7 files changed

+714
-0
lines changed

7 files changed

+714
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.vladmihalcea.hibernate.type.basic;
2+
3+
import com.vladmihalcea.hibernate.type.AbstractHibernateType;
4+
import com.vladmihalcea.hibernate.type.basic.internal.MonthDayTypeDescriptor;
5+
import com.vladmihalcea.hibernate.type.util.Configuration;
6+
import org.hibernate.type.descriptor.sql.DateTypeDescriptor;
7+
8+
import java.time.MonthDay;
9+
10+
/**
11+
* Maps a Java {@link java.time.MonthDay} object to a {@code DATE} column type.
12+
*
13+
* @author Mladen Savic ([email protected])
14+
*/
15+
16+
public class MonthDayDateType extends AbstractHibernateType<MonthDay> {
17+
18+
public static final MonthDayDateType INSTANCE = new MonthDayDateType();
19+
20+
21+
public MonthDayDateType() {
22+
super(DateTypeDescriptor.INSTANCE, MonthDayTypeDescriptor.INSTANCE);
23+
}
24+
25+
public MonthDayDateType(Configuration configuration) {
26+
super(DateTypeDescriptor.INSTANCE, MonthDayTypeDescriptor.INSTANCE, configuration);
27+
}
28+
29+
@Override
30+
public String getName() {
31+
return "monthday-date";
32+
}
33+
34+
@Override
35+
protected boolean registerUnderJavaType() {
36+
return true;
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.vladmihalcea.hibernate.type.basic;
2+
3+
import com.vladmihalcea.hibernate.type.AbstractHibernateType;
4+
import com.vladmihalcea.hibernate.type.basic.internal.MonthDayTypeDescriptor;
5+
import com.vladmihalcea.hibernate.type.util.Configuration;
6+
import org.hibernate.type.descriptor.sql.DateTypeDescriptor;
7+
import org.hibernate.type.descriptor.sql.IntegerTypeDescriptor;
8+
9+
import java.time.MonthDay;
10+
11+
/**
12+
* Maps a Java {@link java.time.MonthDay} object to a {@code INT} column type.
13+
*
14+
* @author Mladen Savic ([email protected])
15+
*/
16+
public class MonthDayIntegerType extends AbstractHibernateType<MonthDay> {
17+
18+
public static final MonthDayIntegerType INSTANCE = new MonthDayIntegerType();
19+
20+
21+
public MonthDayIntegerType() {
22+
super(IntegerTypeDescriptor.INSTANCE, MonthDayTypeDescriptor.INSTANCE);
23+
}
24+
25+
public MonthDayIntegerType(Configuration configuration) {
26+
super(IntegerTypeDescriptor.INSTANCE, MonthDayTypeDescriptor.INSTANCE, configuration);
27+
}
28+
29+
@Override
30+
public String getName() {
31+
return "monthday-int";
32+
}
33+
34+
@Override
35+
protected boolean registerUnderJavaType() {
36+
return true;
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.vladmihalcea.hibernate.type.basic.internal;
2+
3+
import org.hibernate.type.descriptor.WrapperOptions;
4+
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
5+
6+
import java.time.Instant;
7+
import java.time.LocalDate;
8+
import java.time.MonthDay;
9+
import java.time.ZoneId;
10+
import java.util.Date;
11+
import java.util.Objects;
12+
13+
/**
14+
* @author Mladen Savic ([email protected])
15+
*/
16+
17+
public class MonthDayTypeDescriptor extends AbstractTypeDescriptor<MonthDay> {
18+
19+
public static final MonthDayTypeDescriptor INSTANCE = new MonthDayTypeDescriptor();
20+
21+
protected MonthDayTypeDescriptor() {
22+
super(MonthDay.class);
23+
}
24+
25+
@Override
26+
public MonthDay fromString(String s) {
27+
return MonthDay.parse(s);
28+
}
29+
30+
@SuppressWarnings({"unchecked"})
31+
@Override
32+
public <X> X unwrap(MonthDay monthDay, Class<X> type, WrapperOptions wrapperOptions) {
33+
if (monthDay == null) {
34+
return null;
35+
}
36+
if (String.class.isAssignableFrom(type)) {
37+
return (X) toString(monthDay);
38+
}
39+
if (Number.class.isAssignableFrom(type)) {
40+
Integer numericValue = (monthDay.getMonthValue() * 100) + monthDay.getDayOfMonth();
41+
return (X) (numericValue);
42+
}
43+
if (Date.class.isAssignableFrom(type)) {
44+
int currentYear = LocalDate.now().getYear();
45+
return (X) java.sql.Date.valueOf(monthDay.atYear(currentYear));
46+
}
47+
48+
throw unknownUnwrap(type);
49+
}
50+
51+
@Override
52+
public <X> MonthDay wrap(X value, WrapperOptions wrapperOptions) {
53+
if (value == null) {
54+
return null;
55+
}
56+
if (value instanceof String) {
57+
return fromString((String) value);
58+
}
59+
if (value instanceof Number) {
60+
int numericValue = ((Number) (value)).intValue();
61+
int month = numericValue / 100;
62+
int dayOfMonth = numericValue % 100;
63+
return MonthDay.of(month, dayOfMonth);
64+
}
65+
if (value instanceof Date) {
66+
Date date = (Date) value;
67+
return MonthDay.from(Instant.ofEpochMilli(date.getTime())
68+
.atZone(ZoneId.systemDefault())
69+
.toLocalDate());
70+
}
71+
throw unknownWrap(value.getClass());
72+
}
73+
74+
@Override
75+
public boolean areEqual(MonthDay one, MonthDay another) {
76+
return Objects.equals(one, another);
77+
}
78+
79+
@Override
80+
public String toString(MonthDay value) {
81+
return value.toString();
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package com.vladmihalcea.hibernate.type.basic;
2+
3+
import com.vladmihalcea.hibernate.type.util.AbstractMySQLIntegrationTest;
4+
import com.vladmihalcea.hibernate.type.util.AbstractPostgreSQLIntegrationTest;
5+
import org.hibernate.Session;
6+
import org.hibernate.annotations.TypeDef;
7+
import org.junit.Test;
8+
9+
import javax.persistence.*;
10+
import java.time.MonthDay;
11+
import java.util.ArrayList;
12+
import java.util.TimeZone;
13+
14+
import static org.junit.Assert.assertEquals;
15+
16+
/**
17+
* @author Mladen Savic ([email protected])
18+
*/
19+
public class MySQLMonthDayDateTest extends AbstractMySQLIntegrationTest {
20+
21+
public static final String COLUMN_TYPE = "date";
22+
23+
@Override
24+
protected Class<?>[] entities() {
25+
return new Class<?>[]{Season.class};
26+
}
27+
28+
private TimeZone defaultTimeZone;
29+
30+
@Override
31+
protected void afterInit() {
32+
defaultTimeZone = TimeZone.getDefault();
33+
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Athens"));
34+
}
35+
36+
@Override
37+
public void destroy() {
38+
super.destroy();
39+
TimeZone.setDefault(defaultTimeZone);
40+
}
41+
42+
@Test
43+
public void testCreationAndFetchById(){
44+
Season season = createEntity("Summer", MonthDay.of(6,21), MonthDay.of(9,22));
45+
46+
doInJPA(entityManager -> {
47+
Season summer = entityManager.unwrap(Session.class).find(Season.class, season.getId());
48+
49+
assertEquals(summer.getBeginningOfSeason(), MonthDay.of(6,21));
50+
assertEquals(summer.getEndOfSeason(), MonthDay.of(9,22));
51+
});
52+
53+
assertEquals(COLUMN_TYPE, getColumnType("end_of_season") );
54+
assertEquals(COLUMN_TYPE, getColumnType("beginning_of_season") );
55+
56+
}
57+
58+
@Test
59+
public void testFetchWithQuery(){
60+
createEntity("Winter", MonthDay.of(12,21), MonthDay.of(3,20));
61+
62+
doInJPA(entityManager -> {
63+
Season seasonQ = entityManager
64+
.createQuery(
65+
"select s " +
66+
"from Season s " +
67+
"where " +
68+
"s.beginningOfSeason = :beginningOfSeason", Season.class)
69+
.setParameter("beginningOfSeason", MonthDay.of(12,21))
70+
.getSingleResult();
71+
72+
assertEquals("Winter", seasonQ.getName());
73+
});
74+
75+
assertEquals(COLUMN_TYPE, getColumnType("end_of_season") );
76+
assertEquals(COLUMN_TYPE, getColumnType("beginning_of_season") );
77+
}
78+
79+
public Season createEntity(String name, MonthDay beginning, MonthDay end){
80+
Season season = new Season();
81+
season.setName(name);
82+
season.setBeginningOfSeason(beginning);
83+
season.setEndOfSeason(end);
84+
85+
doInJPA(entityManager -> {
86+
entityManager.persist(season);
87+
});
88+
89+
return season;
90+
}
91+
92+
public String getColumnType(String column){
93+
ArrayList<String> results = new ArrayList<>(1);
94+
doInJPA(entityManager -> {
95+
Object result = entityManager.createNativeQuery("SELECT data_type FROM information_schema.columns WHERE \n" +
96+
"table_name = 'season' AND column_name = :column_name")
97+
.setParameter("column_name", column)
98+
.getSingleResult();
99+
results.add((String) result);
100+
});
101+
return results.get(0);
102+
}
103+
104+
105+
@Entity(name = "Season")
106+
@Table(name = "season")
107+
@TypeDef(typeClass = MonthDayDateType.class, defaultForType = MonthDay.class)
108+
public static class Season {
109+
110+
@Id
111+
@GeneratedValue
112+
private Long id;
113+
private String name;
114+
@Column(name = "beginning_of_season", columnDefinition = "date")
115+
private MonthDay beginningOfSeason;
116+
@Column(name = "end_of_season", columnDefinition = "date")
117+
private MonthDay endOfSeason;
118+
119+
public Long getId() {
120+
return id;
121+
}
122+
123+
public void setId(Long id) {
124+
this.id = id;
125+
}
126+
127+
public String getName() {
128+
return name;
129+
}
130+
131+
public void setName(String name) {
132+
this.name = name;
133+
}
134+
135+
public MonthDay getBeginningOfSeason() {
136+
return beginningOfSeason;
137+
}
138+
139+
public void setBeginningOfSeason(MonthDay beginningOfSeason) {
140+
this.beginningOfSeason = beginningOfSeason;
141+
}
142+
143+
public MonthDay getEndOfSeason() {
144+
return endOfSeason;
145+
}
146+
147+
public void setEndOfSeason(MonthDay endOfSeason) {
148+
this.endOfSeason = endOfSeason;
149+
}
150+
}
151+
}

0 commit comments

Comments
 (0)