Skip to content

Commit

Permalink
Add support for LocalDate[] mappings #406
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewLazarus authored and vladmihalcea committed Mar 23, 2022
1 parent d05c208 commit d9c19e6
Show file tree
Hide file tree
Showing 14 changed files with 292 additions and 36 deletions.
@@ -0,0 +1,50 @@
package com.vladmihalcea.hibernate.type.array;

import com.vladmihalcea.hibernate.type.array.internal.AbstractArrayType;
import com.vladmihalcea.hibernate.type.array.internal.LocalDateArrayTypeDescriptor;
import com.vladmihalcea.hibernate.type.util.Configuration;
import com.vladmihalcea.hibernate.type.util.ParameterizedParameterType;
import org.hibernate.usertype.DynamicParameterizedType;

import java.util.Properties;

/**
* Maps a {@code java.Time.LocalDate[]} array on a PostgreSQL date[] ARRAY type. Multidimensional arrays are
* supported as well, as
* explained in <a href="https://vladmihalcea.com/multidimensional-array-jpa-hibernate/">this article</a>.
* <p>
* For more details about how to use it, check out
* <a href="https://vladmihalcea.com/how-to-map-java-and-sql-arrays-with-jpa-and-hibernate/">this
* article</a> on <a href="https://vladmihalcea.com/">vladmihalcea.com</a>.
*
* @author Andrew Lazarus, based on DateArrayType by Guillaume Briand
*/

public class LocalDateArrayType extends AbstractArrayType<java.time.LocalDate[]> {

public static final LocalDateArrayType INSTANCE =
new LocalDateArrayType();

public LocalDateArrayType() {
super(
new LocalDateArrayTypeDescriptor()
);
}

public LocalDateArrayType(Configuration configuration) {
super(
new LocalDateArrayTypeDescriptor(), configuration
);
}

public LocalDateArrayType(Class arrayClass) {
this();
Properties parameters = new Properties();
parameters.put(DynamicParameterizedType.PARAMETER_TYPE, new ParameterizedParameterType(arrayClass));
setParameterValues(parameters);
}

public String getName() {
return "localdate-array";
}
}
Expand Up @@ -125,7 +125,7 @@ public static Object[] wrapArray(Object originalArray) {
}

/**
* Unwarp {@link Object[]} array to an array of the provided type
* Unwrap {@link Object[]} array to an array of the provided type
*
* @param originalArray original array
* @param arrayClass array class
Expand Down Expand Up @@ -193,6 +193,13 @@ public static <T> T unwrapArray(Object[] originalArray, Class<T> arrayClass) {
Array.set(array, i, objectValue);
}
return array;
} else if (java.time.LocalDate[].class.equals(arrayClass) && java.sql.Date[].class.equals(originalArray.getClass())) {
// special case because conversion is neither with ctor nor valueOf
Object[] array = (Object[]) Array.newInstance(java.time.LocalDate.class, originalArray.length);
for (int i = 0; i < array.length; ++i) {
array[i] = originalArray[i] != null ? ((java.sql.Date) originalArray[i]).toLocalDate() : null;
}
return (T) array;
} else if(arrayClass.getComponentType() != null && arrayClass.getComponentType().isArray()) {
int arrayLength = originalArray.length;
Object[] array = (Object[]) Array.newInstance(arrayClass.getComponentType(), arrayLength);
Expand Down
Expand Up @@ -10,6 +10,7 @@
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;

/**
Expand Down Expand Up @@ -130,6 +131,8 @@ public void setParameterValues(Properties parameters) {
sqlArrayType = "boolean";
} else if (BigDecimal.class.isAssignableFrom(arrayElementClass)) {
sqlArrayType = "decimal";
} else if (LocalDate.class.isAssignableFrom(arrayElementClass)) {
sqlArrayType = "date";
} else {
throw new UnsupportedOperationException("The " + arrayElementClass + " is not supported yet!");
}
Expand Down
@@ -0,0 +1,17 @@
package com.vladmihalcea.hibernate.type.array.internal;

/**
* @author Vlad Mihalcea
*/
public class LocalDateArrayTypeDescriptor
extends AbstractArrayTypeDescriptor<java.time.LocalDate[]> {

public LocalDateArrayTypeDescriptor() {
super(java.time.LocalDate[].class);
}

@Override
protected String getSqlArrayType() {
return "date";
}
}
Expand Up @@ -95,6 +95,7 @@ public void test() {
event.setDateValues(new Date[]{date1, date2});
event.setTimestampValues(new Date[]{date1, date2});
event.setDecimalValues(new BigDecimal[]{BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.TEN});
event.setLocalDateValues(new LocalDate[]{LocalDate.of(2022, 3, 22), LocalDate.of(2021, 4, 21)});
event.setSensorBooleanValues(new Boolean[]{false, true, true});

entityManager.persist(event);
Expand All @@ -112,6 +113,7 @@ public void test() {
assertArrayEquals(new Date[]{date1, date2}, event.getDateValues());
assertArrayEquals(new Date[]{date1, date2}, event.getTimestampValues());
assertArrayEquals(new BigDecimal[]{BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.TEN}, event.getDecimalValues());
assertArrayEquals(new LocalDate[]{LocalDate.of(2022, 3, 22), LocalDate.of(2021, 4, 21)}, event.getLocalDateValues());
assertArrayEquals(new Boolean[]{false, true, true}, event.getSensorBooleanValues());
});

Expand Down Expand Up @@ -201,6 +203,10 @@ public static class Event extends BaseEntity {
@Column(name = "decimal_values", columnDefinition = "decimal[]")
private BigDecimal[] decimalValues;

@Type(type = "localdate-array")
@Column(name = "localdate_values", columnDefinition = "date[]")
private LocalDate[] localDateValues;

@Type(type = "sensor-state-array")
@Column(name = "sensor_states", columnDefinition = "sensor_state[]")
private SensorState[] sensorStates;
Expand Down Expand Up @@ -280,6 +286,14 @@ public BigDecimal[] getDecimalValues() {
public void setDecimalValues(BigDecimal[] decimalValues) {
this.decimalValues = decimalValues;
}

public LocalDate[] getLocalDateValues() {
return localDateValues;
}

public void setLocalDateValues(LocalDate[] localDateValues) {
this.localDateValues = localDateValues;
}
}

public enum SensorState {
Expand Down
Expand Up @@ -123,6 +123,12 @@ public void test() {
BigDecimal.TEN
)
)
.setLocalDateValues(
Arrays.asList(
LocalDate.of(2022, 3, 22),
LocalDate.of(2021, 4, 21)
)
)
);
});

Expand Down Expand Up @@ -195,6 +201,13 @@ public void test() {
),
event.getDecimalValues()
);
assertEquals(
Arrays.asList(
LocalDate.of(2022, 3, 22),
LocalDate.of(2021, 4, 21)
),
event.getLocalDateValues()
);
});

doInJPA(entityManager -> {
Expand Down Expand Up @@ -245,6 +258,7 @@ public void testMixingNullValues() {
event.setDateValues(Arrays.asList(null, date));
event.setTimestampValues(Arrays.asList(null, date));
event.setDecimalValues(Arrays.asList(null, BigDecimal.TEN));
event.setLocalDateValues(Arrays.asList(null, LocalDate.of(2021, 4, 21)));
entityManager.persist(event);
});

Expand All @@ -261,6 +275,7 @@ public void testMixingNullValues() {
assertArrayEquals(new Date[]{null, date}, event.getDateValues().toArray());
assertArrayEquals(new Date[]{null, date}, event.getTimestampValues().toArray());
assertArrayEquals(new BigDecimal[]{null, BigDecimal.TEN}, event.getDecimalValues().toArray());
assertArrayEquals(new LocalDate[]{null, LocalDate.of(2021, 4, 21)}, event.getLocalDateValues().toArray());
});
}

Expand All @@ -284,6 +299,7 @@ public void testNullValues() {
event.setDateValues(Arrays.asList(null, null));
event.setTimestampValues(Arrays.asList(null, null));
event.setDecimalValues(Arrays.asList(null, null));
event.setLocalDateValues(Arrays.asList(null, null));
entityManager.persist(event);
});

Expand All @@ -300,6 +316,7 @@ public void testNullValues() {
assertArrayEquals(new Date[]{null, null}, event.getDateValues().toArray());
assertArrayEquals(new Date[]{null, null}, event.getTimestampValues().toArray());
assertArrayEquals(new BigDecimal[]{null, null}, event.getDecimalValues().toArray());
assertArrayEquals(new LocalDate[]{null, null}, event.getLocalDateValues().toArray());
});
}

Expand All @@ -323,6 +340,7 @@ public void testEmptyArrays() {
event.setDateValues(Collections.emptyList());
event.setTimestampValues(Collections.emptyList());
event.setDecimalValues(Collections.emptyList());
event.setLocalDateValues(Collections.emptyList());
entityManager.persist(event);
});

Expand All @@ -339,6 +357,7 @@ public void testEmptyArrays() {
assertArrayEquals(new Date[]{}, event.getDateValues().toArray());
assertArrayEquals(new Date[]{}, event.getTimestampValues().toArray());
assertArrayEquals(new BigDecimal[]{}, event.getDecimalValues().toArray());
assertArrayEquals(new LocalDate[]{}, event.getLocalDateValues().toArray());
});
}

Expand Down Expand Up @@ -429,6 +448,13 @@ public static class Event {
)
private List<BigDecimal> decimalValues;

@Type(type = "list-array")
@Column(
name = "localdate_values",
columnDefinition = "date[]"
)
private List<LocalDate> localDateValues;

public Long getId() {
return id;
}
Expand Down Expand Up @@ -528,6 +554,14 @@ public Event setDecimalValues(List<BigDecimal> decimalValues) {
return this;
}

public List<LocalDate> getLocalDateValues() {
return localDateValues;
}

public Event setLocalDateValues(List<LocalDate> localDateValues) {
this.localDateValues = localDateValues;
return this;
}
}

@Test
Expand Down
Expand Up @@ -22,6 +22,7 @@
@TypeDef(name = "date-array", typeClass = DateArrayType.class),
@TypeDef(name = "timestamp-array", typeClass = TimestampArrayType.class),
@TypeDef(name = "decimal-array", typeClass = DecimalArrayType.class),
@TypeDef(name = "localdate-array", typeClass = LocalDateArrayType.class),
@TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
@TypeDef(name = "jsonb-node", typeClass = JsonNodeBinaryType.class),
Expand Down
@@ -0,0 +1,54 @@
package com.vladmihalcea.hibernate.type.array;

import com.vladmihalcea.hibernate.type.array.internal.AbstractArrayType;
import com.vladmihalcea.hibernate.type.array.internal.LocalDateArrayTypeDescriptor;
import com.vladmihalcea.hibernate.type.util.Configuration;
import com.vladmihalcea.hibernate.type.util.ParameterizedParameterType;
import org.hibernate.usertype.DynamicParameterizedType;

import java.util.Properties;

/**
* Maps a {@code java.Time.LocalDate[]} array on a PostgreSQL date[] ARRAY type. Multidimensional arrays are
* supported as well, as
* explained in <a href="https://vladmihalcea.com/multidimensional-array-jpa-hibernate/">this article</a>.
* <p>
* For more details about how to use it, check out
* <a href="https://vladmihalcea.com/how-to-map-java-and-sql-arrays-with-jpa-and-hibernate/">this
* article</a> on <a href="https://vladmihalcea.com/">vladmihalcea.com</a>.
*
* @author Andrew Lazarus, based on DateArrayType by Guillaume Briand
*/

public class LocalDateArrayType extends AbstractArrayType<java.time.LocalDate[]> {

public static final com.vladmihalcea.hibernate.type.array.LocalDateArrayType INSTANCE =
new com.vladmihalcea.hibernate.type.array.LocalDateArrayType();

public LocalDateArrayType() {
super(
new LocalDateArrayTypeDescriptor()
);
}

public LocalDateArrayType(Configuration configuration) {
super(
new LocalDateArrayTypeDescriptor(), configuration
);
}

public LocalDateArrayType(Class arrayClass) {
this();
Properties parameters = new Properties();
parameters.put(DynamicParameterizedType.PARAMETER_TYPE, new ParameterizedParameterType(arrayClass));
setParameterValues(parameters);
}

public LocalDateArrayType(org.hibernate.type.spi.TypeBootstrapContext typeBootstrapContext) {
this(new Configuration(typeBootstrapContext.getConfigurationSettings()));
}

public String getName() {
return "localdate-array";
}
}
@@ -1,6 +1,6 @@
package com.vladmihalcea.hibernate.type.array.internal;

import java.lang.reflect.Array;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -125,7 +125,7 @@ public static Object[] wrapArray(Object originalArray) {
}

/**
* Unwarp {@link Object[]} array to an array of the provided type
* Unwrap {@link Object[]} array to an array of the provided type
*
* @param originalArray original array
* @param arrayClass array class
Expand Down Expand Up @@ -193,6 +193,13 @@ public static <T> T unwrapArray(Object[] originalArray, Class<T> arrayClass) {
Array.set(array, i, objectValue);
}
return array;
} else if (java.time.LocalDate[].class.equals(arrayClass) && java.sql.Date[].class.equals(originalArray.getClass())) {
// special case because conversion is neither with ctor nor valueOf
Object[] array = (Object[]) Array.newInstance(java.time.LocalDate.class, originalArray.length);
for (int i = 0; i < array.length; ++i) {
array[i] = originalArray[i] != null ? ((java.sql.Date) originalArray[i]).toLocalDate() : null;
}
return (T) array;
} else if(arrayClass.getComponentType() != null && arrayClass.getComponentType().isArray()) {
int arrayLength = originalArray.length;
Object[] array = (Object[]) Array.newInstance(arrayClass.getComponentType(), arrayLength);
Expand All @@ -203,7 +210,7 @@ public static <T> T unwrapArray(Object[] originalArray, Class<T> arrayClass) {
}
return (T) array;
} else {
if(arrayClass.isInstance(originalArray)) {
if (arrayClass.isInstance(originalArray)) {
return (T) originalArray;
} else {
return (T) Arrays.copyOf(originalArray, originalArray.length, (Class) arrayClass);
Expand All @@ -214,9 +221,9 @@ public static <T> T unwrapArray(Object[] originalArray, Class<T> arrayClass) {
/**
* Create array from its {@link String} representation.
*
* @param string string representation
* @param string string representation
* @param arrayClass array class
* @param <T> array element type
* @param <T> array element type
* @return array
*/
public static <T> T fromString(String string, Class<T> arrayClass) {
Expand Down
Expand Up @@ -10,6 +10,7 @@
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;

/**
Expand Down Expand Up @@ -130,6 +131,8 @@ public void setParameterValues(Properties parameters) {
sqlArrayType = "boolean";
} else if (BigDecimal.class.isAssignableFrom(arrayElementClass)) {
sqlArrayType = "decimal";
} else if (LocalDate.class.isAssignableFrom(arrayElementClass)) {
sqlArrayType = "date";
} else {
throw new UnsupportedOperationException("The " + arrayElementClass + " is not supported yet!");
}
Expand Down

0 comments on commit d9c19e6

Please sign in to comment.