Do not convert binary data in List<byte[]> to array SQL type.

byte[] is mapped to BINARY data and is not considered tuple data.

The explicit check for byte[] is not very nice but matches the special handling in MappingJdbcConverter.writeJdbcValue

Closes #1900
Original pull request: #1903
This commit is contained in:
Jens Schauder
2024-10-01 09:57:25 +02:00
committed by Mark Paluch
parent 37c3095cb9
commit 5204336acc
10 changed files with 77 additions and 22 deletions

View File

@@ -93,7 +93,8 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
@Nullable RowMapper<?> defaultRowMapper, JdbcConverter converter,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
this(queryMethod, operations, result -> (RowMapper<Object>) defaultRowMapper, converter, evaluationContextProvider);
this(queryMethod, operations, result -> (RowMapper<Object>) defaultRowMapper,
converter, evaluationContextProvider);
}
/**
@@ -221,7 +222,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
TypeInformation<?> actualType = typeInformation.getActualType();
// tuple-binding
if (actualType != null && actualType.getType().isArray()) {
if (actualType != null && actualType.getType().isArray() && !actualType.getType().equals(byte[].class)) {
TypeInformation<?> nestedElementType = actualType.getRequiredActualType();
return writeCollection(collection, parameter.getActualSqlType(),

View File

@@ -41,7 +41,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationListener;
@@ -51,23 +50,13 @@ import org.springframework.context.annotation.Import;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Limit;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Window;
import org.springframework.data.domain.*;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.jdbc.testing.ConditionalOnDatabase;
import org.springframework.data.jdbc.testing.DatabaseType;
import org.springframework.data.jdbc.testing.EnabledOnDatabase;
import org.springframework.data.jdbc.testing.EnabledOnFeature;
import org.springframework.data.jdbc.testing.IntegrationTest;
import org.springframework.data.jdbc.testing.TestConfiguration;
@@ -1357,6 +1346,52 @@ public class JdbcRepositoryIntegrationTests {
assertThat(result).containsOnly(two);
}
@Test // GH-1900
void queryByListOfByteArray() {
byte[] oneBytes = { 1, 2, 3, 4, 5, 6, 7, 8 };
DummyEntity one = createDummyEntity("one");
one.setBytes(oneBytes);
one = repository.save(one);
byte[] twoBytes = { 8, 7, 6, 5, 4, 3, 2, 1 };
DummyEntity two = createDummyEntity("two");
two.setBytes(twoBytes);
two = repository.save(two);
byte[] threeBytes = { 3, 3, 3, 3, 3, 3, 3, 3 };
DummyEntity three = createDummyEntity("three");
three.setBytes(threeBytes);
three = repository.save(three);
List<DummyEntity> result = repository.findByBytesIn(List.of(threeBytes, oneBytes));
assertThat(result).extracting("idProp").containsExactlyInAnyOrder(one.idProp, three.idProp);
}
@Test // GH-1900
void queryByByteArray() {
byte[] oneBytes = { 1, 2, 3, 4, 5, 6, 7, 8 };
DummyEntity one = createDummyEntity("one");
one.setBytes(oneBytes);
one = repository.save(one);
byte[] twoBytes = { 8, 7, 6, 5, 4, 3, 2, 1 };
DummyEntity two = createDummyEntity("two");
two.setBytes(twoBytes);
two = repository.save(two);
byte[] threeBytes = { 3, 3, 3, 3, 3, 3, 3, 3 };
DummyEntity three = createDummyEntity("three");
three.setBytes(threeBytes);
three = repository.save(three);
List<DummyEntity> result = repository.findByBytes(twoBytes);
assertThat(result).extracting("idProp").containsExactly(two.idProp);
}
private Root createRoot(String namePrefix) {
return new Root(null, namePrefix,
@@ -1487,6 +1522,12 @@ public class JdbcRepositoryIntegrationTests {
@Query("SELECT * FROM DUMMY_ENTITY WHERE (ID_PROP, NAME) IN (:tuples)")
List<DummyEntity> findByListInTuple(List<Object[]> tuples);
@Query("SELECT * FROM DUMMY_ENTITY WHERE BYTES IN (:bytes)")
List<DummyEntity> findByBytesIn(List<byte[]> bytes);
@Query("SELECT * FROM DUMMY_ENTITY WHERE BYTES = :bytes")
List<DummyEntity> findByBytes(byte[] bytes);
}
interface RootRepository extends ListCrudRepository<Root, Long> {
@@ -1810,6 +1851,7 @@ public class JdbcRepositoryIntegrationTests {
boolean flag;
AggregateReference<DummyEntity, Long> ref;
Direction direction;
byte[] bytes = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
public DummyEntity(String name) {
this.name = name;
@@ -1890,6 +1932,10 @@ public class JdbcRepositoryIntegrationTests {
return Objects.hash(name, pointInTime, offsetDateTime, idProp, flag, ref, direction);
}
public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
@Override
public String toString() {
return "DummyEntity{" + "name='" + name + '\'' + ", idProp=" + idProp + '}';

View File

@@ -12,7 +12,8 @@ CREATE TABLE dummy_entity
OFFSET_DATE_TIME TIMESTAMP, -- with time zone is only supported with z/OS
FLAG BOOLEAN,
REF BIGINT,
DIRECTION VARCHAR(100)
DIRECTION VARCHAR(100),
BYTES BINARY(8)
);
CREATE TABLE ROOT

View File

@@ -6,7 +6,8 @@ CREATE TABLE dummy_entity
OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE,
FLAG BOOLEAN,
REF BIGINT,
DIRECTION VARCHAR(100)
DIRECTION VARCHAR(100),
BYTES BINARY(8)
);
CREATE TABLE ROOT

View File

@@ -6,7 +6,8 @@ CREATE TABLE dummy_entity
OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE,
FLAG BOOLEAN,
REF BIGINT,
DIRECTION VARCHAR(100)
DIRECTION VARCHAR(100),
BYTES BINARY(8)
);
CREATE TABLE ROOT

View File

@@ -6,7 +6,8 @@ CREATE TABLE dummy_entity
OFFSET_DATE_TIME TIMESTAMP(3),
FLAG BOOLEAN,
REF BIGINT,
DIRECTION VARCHAR(100)
DIRECTION VARCHAR(100),
BYTES BINARY(8)
);
CREATE TABLE ROOT

View File

@@ -12,7 +12,8 @@ CREATE TABLE dummy_entity
OFFSET_DATE_TIME DATETIMEOFFSET,
FLAG BIT,
REF BIGINT,
DIRECTION VARCHAR(100)
DIRECTION VARCHAR(100),
BYTES VARBINARY(8)
);
CREATE TABLE ROOT

View File

@@ -9,7 +9,8 @@ CREATE TABLE DUMMY_ENTITY
OFFSET_DATE_TIME TIMESTAMP(3) DEFAULT NULL,
FLAG BIT(1),
REF BIGINT,
DIRECTION VARCHAR(100)
DIRECTION VARCHAR(100),
BYTES BINARY(8)
);
CREATE TABLE ROOT

View File

@@ -12,7 +12,8 @@ CREATE TABLE DUMMY_ENTITY
OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE,
FLAG NUMBER(1,0),
REF NUMBER,
DIRECTION VARCHAR2(100)
DIRECTION VARCHAR2(100),
BYTES RAW(8)
);
CREATE TABLE ROOT

View File

@@ -12,7 +12,8 @@ CREATE TABLE dummy_entity
OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE,
FLAG BOOLEAN,
REF BIGINT,
DIRECTION VARCHAR(100)
DIRECTION VARCHAR(100),
BYTES BYTEA
);
CREATE TABLE ROOT