Browse Source

Polishing.

Refactor ArrayUtil into a proper utility class providing toPrimitive and toObject methods for each primitive type. Add boolean support. Move convertToObjectArray to BasicJdbcConverter as its placement in ArrayUtils creates a certain amount of ambiguity over its actual purpose.

Create unit test.

See #945
Original pull request: #949.
pull/1035/head
Mark Paluch 5 years ago
parent
commit
e99455538b
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 138
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ArrayUtil.java
  2. 464
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ArrayUtils.java
  3. 42
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
  4. 55
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/ArrayUtilsUnitTests.java

138
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ArrayUtil.java

@ -1,138 +0,0 @@ @@ -1,138 +0,0 @@
/*
* Copyright 2019-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.convert;
/**
* A collection of utility methods for dealing with arrays.
*
* @author Jens Schauder
* @since 1.1
*/
final class ArrayUtil {
private ArrayUtil() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
/**
* Converts an {@code Byte[]} into a {@code byte[]}.
*
* @param byteArray the array to be converted. Must not be {@literal null}.
* @return a {@code byte[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static byte[] toPrimitiveByteArray(Byte[] byteArray) {
byte[] bytes = new byte[byteArray.length];
for (int i = 0; i < byteArray.length; i++) {
bytes[i] = byteArray[i];
}
return bytes;
}
static Byte[] toObjectArray(byte[] primitiveArray) {
Byte[] objects = new Byte[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; i++) {
objects[i] = primitiveArray[i];
}
return objects;
}
static Short[] toObjectArray(short[] primitiveArray) {
Short[] objects = new Short[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; i++) {
objects[i] = primitiveArray[i];
}
return objects;
}
static Character[] toObjectArray(char[] primitiveArray) {
Character[] objects = new Character[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; i++) {
objects[i] = primitiveArray[i];
}
return objects;
}
static Integer[] toObjectArray(int[] primitiveArray) {
Integer[] objects = new Integer[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; i++) {
objects[i] = primitiveArray[i];
}
return objects;
}
static Long[] toObjectArray(long[] primitiveArray) {
Long[] objects = new Long[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; i++) {
objects[i] = primitiveArray[i];
}
return objects;
}
static Float[] toObjectArray(float[] primitiveArray) {
Float[] objects = new Float[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; i++) {
objects[i] = primitiveArray[i];
}
return objects;
}
static Double[] toObjectArray(double[] primitiveArray) {
Double[] objects = new Double[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; i++) {
objects[i] = primitiveArray[i];
}
return objects;
}
static Object[] convertToObjectArray(Object unknownArray) {
Class<?> componentType = unknownArray.getClass().getComponentType();
if (componentType.isPrimitive()) {
if (componentType == byte.class) {
return toObjectArray((byte[]) unknownArray);
}
if (componentType == short.class) {
return toObjectArray((short[]) unknownArray);
}
if (componentType == char.class) {
return toObjectArray((char[]) unknownArray);
}
if (componentType == int.class) {
return toObjectArray((int[]) unknownArray);
}
if (componentType == long.class) {
return toObjectArray((long[]) unknownArray);
}
if (componentType == float.class) {
return toObjectArray((float[]) unknownArray);
}
if (componentType == double.class) {
return toObjectArray((double[]) unknownArray);
}
}
return (Object[]) unknownArray;
}
}

464
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ArrayUtils.java

@ -0,0 +1,464 @@ @@ -0,0 +1,464 @@
/*
* Copyright 2019-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.convert;
import org.springframework.util.Assert;
/**
* A collection of utility methods for dealing with arrays.
* <p>
* Mainly for internal use within the framework.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
final class ArrayUtils {
/**
* An empty immutable {@code boolean} array.
*/
public static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
/**
* An empty immutable {@link Boolean} array.
*/
public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = new Boolean[0];
/**
* An empty immutable {@code byte} array.
*/
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/**
* An empty immutable {@link Byte} array.
*/
public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = new Byte[0];
/**
* An empty immutable {@code char} array.
*/
public static final char[] EMPTY_CHAR_ARRAY = new char[0];
/**
* An empty immutable {@link Character} array.
*/
public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0];
/**
* An empty immutable {@code double} array.
*/
public static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
/**
* An empty immutable {@code Double} array.
*/
public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = new Double[0];
/**
* An empty immutable {@code float} array.
*/
public static final float[] EMPTY_FLOAT_ARRAY = new float[0];
/**
* An empty immutable {@code Float} array.
*/
public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = new Float[0];
/**
* An empty immutable {@code int} array.
*/
public static final int[] EMPTY_INT_ARRAY = new int[0];
/**
* An empty immutable {@link Integer} array.
*/
public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = new Integer[0];
/**
* An empty immutable {@code long} array.
*/
public static final long[] EMPTY_LONG_ARRAY = new long[0];
/**
* An empty immutable {@link Long} array.
*/
public static final Long[] EMPTY_LONG_OBJECT_ARRAY = new Long[0];
/**
* An empty immutable {@code short} array.
*/
public static final short[] EMPTY_SHORT_ARRAY = new short[0];
/**
* An empty immutable {@link Short} array.
*/
public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = new Short[0];
private ArrayUtils() {
}
/**
* Converts an {@code Boolean[]} into a {@code boolean[]}.
*
* @param array the array to be converted. Must not be {@literal null} and must not contain {@literal null} elements.
* @return a {@code boolean[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static boolean[] toPrimitive(Boolean[] array) {
Assert.noNullElements(array, "Array must not contain null elements");
if (array.length == 0) {
return EMPTY_BOOLEAN_ARRAY;
}
boolean[] booleans = new boolean[array.length];
for (int i = 0; i < array.length; i++) {
booleans[i] = array[i];
}
return booleans;
}
/**
* Converts an {@code boolean[]} into a {@code Boolean[]}.
*
* @param array the array to be converted. Must not be {@literal null}.
* @return a {@code Boolean[]} of same size with the boxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static Boolean[] toObject(boolean[] array) {
if (array.length == 0) {
return EMPTY_BOOLEAN_OBJECT_ARRAY;
}
Boolean[] booleans = new Boolean[array.length];
for (int i = 0; i < array.length; i++) {
booleans[i] = array[i];
}
return booleans;
}
/**
* Converts an {@code Byte[]} into a {@code byte[]}.
*
* @param array the array to be converted. Must not be {@literal null} and must not contain {@literal null} elements.
* @return a {@code byte[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static byte[] toPrimitive(Byte[] array) {
Assert.noNullElements(array, "Array must not contain null elements");
if (array.length == 0) {
return EMPTY_BYTE_ARRAY;
}
byte[] bytes = new byte[array.length];
for (int i = 0; i < array.length; i++) {
bytes[i] = array[i];
}
return bytes;
}
/**
* Converts an {@code byte[]} into a {@code Byte[]}.
*
* @param array the array to be converted. Must not be {@literal null}.
* @return a {@code Byte[]} of same size with the boxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static Byte[] toObject(byte[] array) {
if (array.length == 0) {
return EMPTY_BYTE_OBJECT_ARRAY;
}
Byte[] bytes = new Byte[array.length];
for (int i = 0; i < array.length; i++) {
bytes[i] = array[i];
}
return bytes;
}
/**
* Converts an {@code Character[]} into a {@code char[]}.
*
* @param array the array to be converted. Must not be {@literal null} and must not contain {@literal null} elements.
* @return a {@code char[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static char[] toPrimitive(Character[] array) {
Assert.noNullElements(array, "Array must not contain null elements");
if (array.length == 0) {
return EMPTY_CHAR_ARRAY;
}
char[] chars = new char[array.length];
for (int i = 0; i < array.length; i++) {
chars[i] = array[i];
}
return chars;
}
/**
* Converts an {@code char[]} into a {@code Character[]}.
*
* @param array the array to be converted. Must not be {@literal null}.
* @return a {@code Character[]} of same size with the boxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static Character[] toObject(char[] array) {
if (array.length == 0) {
return EMPTY_CHARACTER_OBJECT_ARRAY;
}
Character[] objects = new Character[array.length];
for (int i = 0; i < array.length; i++) {
objects[i] = array[i];
}
return objects;
}
/**
* Converts an {@code Double[]} into a {@code double[]}.
*
* @param array the array to be converted. Must not be {@literal null} and must not contain {@literal null} elements.
* @return a {@code double[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static double[] toPrimitive(Double[] array) {
Assert.noNullElements(array, "Array must not contain null elements");
if (array.length == 0) {
return EMPTY_DOUBLE_ARRAY;
}
double[] doubles = new double[array.length];
for (int i = 0; i < array.length; i++) {
doubles[i] = array[i];
}
return doubles;
}
/**
* Converts an {@code double[]} into a {@code Double[]}.
*
* @param array the array to be converted. Must not be {@literal null}.
* @return a {@code Double[]} of same size with the boxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static Double[] toObject(double[] array) {
if (array.length == 0) {
return EMPTY_DOUBLE_OBJECT_ARRAY;
}
Double[] objects = new Double[array.length];
for (int i = 0; i < array.length; i++) {
objects[i] = array[i];
}
return objects;
}
/**
* Converts an {@code Float[]} into a {@code float[]}.
*
* @param array the array to be converted. Must not be {@literal null} and must not contain {@literal null} elements.
* @return a {@code float[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static float[] toPrimitive(Float[] array) {
Assert.noNullElements(array, "Array must not contain null elements");
if (array.length == 0) {
return EMPTY_FLOAT_ARRAY;
}
float[] floats = new float[array.length];
for (int i = 0; i < array.length; i++) {
floats[i] = array[i];
}
return floats;
}
/**
* Converts an {@code float[]} into a {@code Float[]}.
*
* @param array the array to be converted. Must not be {@literal null}.
* @return a {@code Float[]} of same size with the boxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static Float[] toObject(float[] array) {
if (array.length == 0) {
return EMPTY_FLOAT_OBJECT_ARRAY;
}
Float[] objects = new Float[array.length];
for (int i = 0; i < array.length; i++) {
objects[i] = array[i];
}
return objects;
}
/**
* Converts an {@code Integer[]} into a {@code int[]}.
*
* @param array the array to be converted. Must not be {@literal null} and must not contain {@literal null} elements.
* @return a {@code int[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static int[] toPrimitive(Integer[] array) {
Assert.noNullElements(array, "Array must not contain null elements");
if (array.length == 0) {
return EMPTY_INT_ARRAY;
}
int[] ints = new int[array.length];
for (int i = 0; i < array.length; i++) {
ints[i] = array[i];
}
return ints;
}
/**
* Converts an {@code int[]} into a {@code Integer[]}.
*
* @param array the array to be converted. Must not be {@literal null}.
* @return a {@code Integer[]} of same size with the boxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static Integer[] toObject(int[] array) {
if (array.length == 0) {
return EMPTY_INTEGER_OBJECT_ARRAY;
}
Integer[] objects = new Integer[array.length];
for (int i = 0; i < array.length; i++) {
objects[i] = array[i];
}
return objects;
}
/**
* Converts an {@code Long[]} into a {@code long[]}.
*
* @param array the array to be converted. Must not be {@literal null} and must not contain {@literal null} elements.
* @return a {@code long[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static long[] toPrimitive(Long[] array) {
Assert.noNullElements(array, "Array must not contain null elements");
if (array.length == 0) {
return EMPTY_LONG_ARRAY;
}
long[] longs = new long[array.length];
for (int i = 0; i < array.length; i++) {
longs[i] = array[i];
}
return longs;
}
/**
* Converts an {@code long[]} into a {@code Long[]}.
*
* @param array the array to be converted. Must not be {@literal null}.
* @return a {@code Long[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static Long[] toObject(long[] array) {
if (array.length == 0) {
return EMPTY_LONG_OBJECT_ARRAY;
}
Long[] objects = new Long[array.length];
for (int i = 0; i < array.length; i++) {
objects[i] = array[i];
}
return objects;
}
/**
* Converts an {@code Short[]} into a {@code short[]}.
*
* @param array the array to be converted. Must not be {@literal null} and must not contain {@literal null} elements.
* @return a {@code short[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static short[] toPrimitive(Short[] array) {
Assert.noNullElements(array, "Array must not contain null elements");
if (array.length == 0) {
return EMPTY_SHORT_ARRAY;
}
short[] shorts = new short[array.length];
for (int i = 0; i < array.length; i++) {
shorts[i] = array[i];
}
return shorts;
}
/**
* Converts an {@code short[]} into a {@code Short[]}.
*
* @param array the array to be converted. Must not be {@literal null}.
* @return a {@code Short[]} of same size with the unboxed values of the input array. Guaranteed to be not
* {@literal null}.
*/
static Short[] toObject(short[] array) {
if (array.length == 0) {
return EMPTY_SHORT_OBJECT_ARRAY;
}
Short[] objects = new Short[array.length];
for (int i = 0; i < array.length; i++) {
objects[i] = array[i];
}
return objects;
}
}

42
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java

@ -317,12 +317,12 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc @@ -317,12 +317,12 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
Class<?> componentType = convertedValue.getClass().getComponentType();
if (componentType != byte.class && componentType != Byte.class) {
Object[] objectArray = ArrayUtil.convertToObjectArray(convertedValue);
Object[] objectArray = requireObjectArray(convertedValue);
return JdbcValue.of(typeFactory.createArray(objectArray), JDBCType.ARRAY);
}
if (componentType == Byte.class) {
convertedValue = ArrayUtil.toPrimitiveByteArray((Byte[]) convertedValue);
convertedValue = ArrayUtils.toPrimitive((Byte[]) convertedValue);
}
return JdbcValue.of(convertedValue, JDBCType.BINARY);
@ -337,7 +337,6 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc @@ -337,7 +337,6 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
if (converted instanceof JdbcValue) {
return (JdbcValue) converted;
}
}
return null;
@ -354,6 +353,43 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc @@ -354,6 +353,43 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
return new ReadingContext<T>(path, new ResultSetAccessor(resultSet), identifier, key).mapRow();
}
static Object[] requireObjectArray(Object source) {
Assert.isTrue(source.getClass().isArray(), "Source object is not an array");
Class<?> componentType = source.getClass().getComponentType();
if (componentType.isPrimitive()) {
if (componentType == boolean.class) {
return ArrayUtils.toObject((boolean[]) source);
}
if (componentType == byte.class) {
return ArrayUtils.toObject((byte[]) source);
}
if (componentType == char.class) {
return ArrayUtils.toObject((char[]) source);
}
if (componentType == double.class) {
return ArrayUtils.toObject((double[]) source);
}
if (componentType == float.class) {
return ArrayUtils.toObject((float[]) source);
}
if (componentType == int.class) {
return ArrayUtils.toObject((int[]) source);
}
if (componentType == long.class) {
return ArrayUtils.toObject((long[]) source);
}
if (componentType == short.class) {
return ArrayUtils.toObject((short[]) source);
}
throw new IllegalArgumentException("Unsupported component type: " + componentType);
}
return (Object[]) source;
}
private class ReadingContext<T> {
private final RelationalPersistentEntity<T> entity;

55
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/ArrayUtilsUnitTests.java

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.convert;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.data.Offset.offset;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link ArrayUtils}.
*
* @author Mark Paluch
*/
class ArrayUtilsUnitTests {
@Test
void testCreatePrimitiveArray() {
assertThat(ArrayUtils.toPrimitive(new Boolean[] { true })).isEqualTo(new boolean[] { true });
assertThat(ArrayUtils.toPrimitive(new Byte[] { 1 })).isEqualTo(new byte[] { 1 });
assertThat(ArrayUtils.toPrimitive(new Character[] { 'a' })).isEqualTo(new char[] { 'a' });
assertThat(ArrayUtils.toPrimitive(new Double[] { 2.718 })).contains(new double[] { 2.718 }, offset(0.1));
assertThat(ArrayUtils.toPrimitive(new Float[] { 3.14f })).contains(new float[] { 3.14f }, offset(0.1f));
assertThat(ArrayUtils.toPrimitive(new Integer[] {})).isEqualTo(new int[] {});
assertThat(ArrayUtils.toPrimitive(new Long[] { 2L, 3L })).isEqualTo(new long[] { 2, 3 });
assertThat(ArrayUtils.toPrimitive(new Short[] { 2 })).isEqualTo(new short[] { 2 });
}
@Test
void testCreatePrimitiveArrayViaObjectArray() {
assertThat(ArrayUtils.toPrimitive(new Boolean[] { true })).isEqualTo(new boolean[] { true });
assertThat(ArrayUtils.toPrimitive(new Byte[] { 1 })).isEqualTo(new byte[] { 1 });
assertThat(ArrayUtils.toPrimitive(new Character[] { 'a' })).isEqualTo(new char[] { 'a' });
assertThat(ArrayUtils.toPrimitive(new Double[] { 2.718 })).contains(new double[] { 2.718 }, offset(0.1));
assertThat(ArrayUtils.toPrimitive(new Float[] { 3.14f })).contains(new float[] { 3.14f }, offset(0.1f));
assertThat(ArrayUtils.toPrimitive(new Integer[] {})).isEqualTo(new int[] {});
assertThat(ArrayUtils.toPrimitive(new Long[] { 2L, 3L })).isEqualTo(new long[] { 2, 3 });
assertThat(ArrayUtils.toPrimitive(new Short[] { 2 })).isEqualTo(new short[] { 2 });
}
}
Loading…
Cancel
Save