1 package com.vladmihalcea.hibernate.type.range;
2
3 import com.vladmihalcea.hibernate.type.ImmutableType;
4 import com.vladmihalcea.hibernate.util.ReflectionUtils;
5 import org.hibernate.HibernateException;
6 import org.hibernate.annotations.common.reflection.XProperty;
7 import org.hibernate.annotations.common.reflection.java.JavaXMember;
8 import org.hibernate.engine.spi.SharedSessionContractImplementor;
9 import org.hibernate.usertype.DynamicParameterizedType;
10
11 import java.lang.reflect.ParameterizedType;
12 import java.lang.reflect.Type;
13 import java.math.BigDecimal;
14 import java.sql.PreparedStatement;
15 import java.sql.ResultSet;
16 import java.sql.SQLException;
17 import java.sql.Types;
18 import java.time.LocalDate;
19 import java.time.LocalDateTime;
20 import java.time.ZonedDateTime;
21 import java.util.Properties;
22
23
44 public class PostgreSQLRangeType extends ImmutableType<Range> implements DynamicParameterizedType {
45
46 public static final PostgreSQLRangeType INSTANCE = new PostgreSQLRangeType();
47
48 private Type type;
49
50 public PostgreSQLRangeType() {
51 super(Range.class);
52 }
53
54 @Override
55 public int[] sqlTypes() {
56 return new int[]{Types.OTHER};
57 }
58
59 @Override
60 protected Range get(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws SQLException {
61 Object pgObject = rs.getObject(names[0]);
62
63 if (pgObject == null) {
64 return null;
65 }
66
67 String type = ReflectionUtils.invokeGetter(pgObject, "type");
68 String value = ReflectionUtils.invokeGetter(pgObject, "value");
69
70 switch (type) {
71 case "int4range":
72 return Range.integerRange(value);
73 case "int8range":
74 return Range.longRange(value);
75 case "numrange":
76 return Range.bigDecimalRange(value);
77 case "tsrange":
78 return Range.localDateTimeRange(value);
79 case "tstzrange":
80 return Range.zonedDateTimeRange(value);
81 case "daterange":
82 return Range.localDateRange(value);
83 default:
84 throw new HibernateException(
85 new IllegalStateException("The range type [" + type + "] is not supported!")
86 );
87 }
88 }
89
90 @Override
91 protected void set(PreparedStatement st, Range range, int index, SharedSessionContractImplementor session) throws SQLException {
92
93 if (range == null) {
94 st.setNull(index, Types.OTHER);
95 } else {
96 Object holder = ReflectionUtils.newInstance("org.postgresql.util.PGobject");
97 ReflectionUtils.invokeSetter(holder, "type", determineRangeType(range));
98 ReflectionUtils.invokeSetter(holder, "value", range.asString());
99 st.setObject(index, holder);
100 }
101 }
102
103 private static String determineRangeType(Range<?> range) {
104 Class<?> clazz = range.getClazz();
105
106 if (clazz.equals(Integer.class)) {
107 return "int4range";
108 } else if (clazz.equals(Long.class)) {
109 return "int8range";
110 } else if (clazz.equals(BigDecimal.class)) {
111 return "numrange";
112 } else if (clazz.equals(LocalDateTime.class)) {
113 return "tsrange";
114 } else if (clazz.equals(ZonedDateTime.class)) {
115 return "tstzrange";
116 } else if (clazz.equals(LocalDate.class)) {
117 return "daterange";
118 }
119
120 throw new HibernateException(
121 new IllegalStateException("The class [" + clazz.getName() + "] is not supported!")
122 );
123 }
124
125 @Override
126 public void setParameterValues(Properties parameters) {
127 final XProperty xProperty = (XProperty) parameters.get(DynamicParameterizedType.XPROPERTY);
128 if (xProperty instanceof JavaXMember) {
129 type = ((JavaXMember) xProperty).getJavaType();
130 } else {
131 type = ((ParameterType) parameters.get(PARAMETER_TYPE)).getReturnedClass();
132 }
133 }
134
135 public Class<?> getElementType() {
136 return type instanceof ParameterizedType ?
137 (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0] : null;
138 }
139
140 }
141