Skip to content

Mitigate performance regressions #1772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>3.3.0-SNAPSHOT</version>
<version>3.3.0-GH-1721-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data Relational Parent</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-jdbc-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>3.3.0-SNAPSHOT</version>
<version>3.3.0-GH-1721-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-jdbc</artifactId>
<version>3.3.0-SNAPSHOT</version>
<version>3.3.0-GH-1721-SNAPSHOT</version>

<name>Spring Data JDBC</name>
<description>Spring Data module for JDBC repositories.</description>
Expand All @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>3.3.0-SNAPSHOT</version>
<version>3.3.0-GH-1721-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ public T mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
RowDocument document = RowDocumentResultSetExtractor.toRowDocument(resultSet);

return identifier == null //
? converter.readAndResolve(entity.getType(), document) //
: converter.readAndResolve(entity.getType(), document, identifier);
? converter.readAndResolve(entity.getTypeInformation(), document, Identifier.empty()) //
: converter.readAndResolve(entity.getTypeInformation(), document, identifier);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.RowDocument;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;

/**
Expand All @@ -47,7 +48,21 @@ public interface JdbcConverter extends RelationalConverter {
* @return The converted value wrapped in a {@link JdbcValue}. Guaranteed to be not {@literal null}.
* @since 2.4
*/
JdbcValue writeJdbcValue(@Nullable Object value, Class<?> type, SQLType sqlType);
default JdbcValue writeJdbcValue(@Nullable Object value, Class<?> type, SQLType sqlType) {
return writeJdbcValue(value, TypeInformation.of(type), sqlType);
}

/**
* Convert a property value into a {@link JdbcValue} that contains the converted value and information how to bind it
* to JDBC parameters.
*
* @param value a value as it is used in the object model. May be {@code null}.
* @param type {@link TypeInformation} into which the value is to be converted. Must not be {@code null}.
* @param sqlType the {@link SQLType} to be used if non is specified by a converter.
* @return The converted value wrapped in a {@link JdbcValue}. Guaranteed to be not {@literal null}.
* @since 3.2.6
*/
JdbcValue writeJdbcValue(@Nullable Object value, TypeInformation<?> type, SQLType sqlType);

/**
* Read the current row from {@link ResultSet} to an {@link RelationalPersistentEntity#getType() entity}.
Expand Down Expand Up @@ -84,7 +99,7 @@ default <T> T mapRow(RelationalPersistentEntity<T> entity, ResultSet resultSet,
default <T> T mapRow(PersistentPropertyPathExtension path, ResultSet resultSet, Identifier identifier, Object key) {

try {
return (T) readAndResolve(path.getRequiredLeafEntity().getType(),
return (T) readAndResolve(path.getRequiredLeafEntity().getTypeInformation(),
RowDocumentResultSetExtractor.toRowDocument(resultSet), identifier);
} catch (SQLException e) {
throw new RuntimeException(e);
Expand Down Expand Up @@ -118,15 +133,31 @@ default <R> R readAndResolve(Class<R> type, RowDocument source) {
* @since 3.2
* @see #read(Class, RowDocument)
*/
<R> R readAndResolve(Class<R> type, RowDocument source, Identifier identifier);
default <R> R readAndResolve(Class<R> type, RowDocument source, Identifier identifier) {
return readAndResolve(TypeInformation.of(type), source, identifier);
}

/**
* Read a {@link RowDocument} into the requested {@link TypeInformation aggregate type} and resolve references by
* looking these up from {@link RelationResolver}.
*
* @param type target aggregate type.
* @param source source {@link RowDocument}.
* @param identifier identifier chain.
* @return the converted object.
* @param <R> aggregate type.
* @since 3.2.6
* @see #read(Class, RowDocument)
*/
<R> R readAndResolve(TypeInformation<R> type, RowDocument source, Identifier identifier);

/**
* The type to be used to store this property in the database. Multidimensional arrays are unwrapped to reflect a
* top-level array type (e.g. {@code String[][]} returns {@code String[]}).
*
* @return a {@link Class} that is suitable for usage with JDBC drivers.
* @see org.springframework.data.jdbc.support.JdbcUtil#targetSqlTypeFor(Class)
* @since 2.0
* @since 2.0 TODO: Introduce variant returning TypeInformation.
*/
Class<?> getColumnType(RelationalPersistentProperty property);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.List;

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.lang.NonNull;
Expand All @@ -57,7 +58,7 @@ public abstract class Jsr310TimestampBasedConverters {
*/
public static Collection<Converter<?, ?>> getConvertersToRegister() {

List<Converter<?, ?>> converters = new ArrayList<>(8);
List<Converter<?, ?>> converters = new ArrayList<>(28);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 28?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meh, that was a leftover from the code below (TODO). We should discuss that one.


converters.add(TimestampToLocalDateTimeConverter.INSTANCE);
converters.add(TimestampToLocalDateConverter.INSTANCE);
Expand All @@ -67,6 +68,10 @@ public abstract class Jsr310TimestampBasedConverters {
converters.add(TimestampToInstantConverter.INSTANCE);
converters.add(InstantToTimestampConverter.INSTANCE);

// TODO: Install some JSR310 converters to avoid ObjectToObjectConverter usage, such as java.sql.Date ->
// LocalDateTime and vice versa
// converters.addAll(Jsr310Converters.getConvertersToRegister());

return converters;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public Map.Entry<Object, T> mapRow(ResultSet rs, int rowNum) throws SQLException
@SuppressWarnings("unchecked")
private T mapEntity(RowDocument document, Object key) {

return (T) converter.readAndResolve(path.getRequiredLeafEntity().getType(), document,
return (T) converter.readAndResolve(path.getRequiredLeafEntity().getTypeInformation(), document,
identifier.withPart(keyColumn, key, Object.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,14 @@ private boolean canWriteAsJdbcValue(@Nullable Object value) {
}

@Override
public JdbcValue writeJdbcValue(@Nullable Object value, Class<?> columnType, SQLType sqlType) {
public JdbcValue writeJdbcValue(@Nullable Object value, TypeInformation<?> columnType, SQLType sqlType) {

JdbcValue jdbcValue = tryToConvertToJdbcValue(value);
if (jdbcValue != null) {
return jdbcValue;
}

Object convertedValue = writeValue(value, TypeInformation.of(columnType));
Object convertedValue = writeValue(value, columnType);

if (convertedValue == null || !convertedValue.getClass().isArray()) {

Expand Down Expand Up @@ -275,7 +275,7 @@ private JdbcValue tryToConvertToJdbcValue(@Nullable Object value) {

@SuppressWarnings("unchecked")
@Override
public <R> R readAndResolve(Class<R> type, RowDocument source, Identifier identifier) {
public <R> R readAndResolve(TypeInformation<R> type, RowDocument source, Identifier identifier) {

RelationalPersistentEntity<R> entity = (RelationalPersistentEntity<R>) getMappingContext()
.getRequiredPersistentEntity(type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;

import org.springframework.core.convert.converter.Converter;
Expand Down Expand Up @@ -83,9 +84,9 @@ public JdbcQueryMethod getQueryMethod() {
*/
@Deprecated(since = "3.1", forRemoval = true)
// a better name would be createReadingQueryExecution
protected JdbcQueryExecution<?> getQueryExecution(JdbcQueryMethod queryMethod,
JdbcQueryExecution<?> getQueryExecution(JdbcQueryMethod queryMethod,
@Nullable ResultSetExtractor<?> extractor, RowMapper<?> rowMapper) {
return createReadingQueryExecution(extractor, rowMapper);
return createReadingQueryExecution(extractor, () -> rowMapper);
}

/**
Expand All @@ -96,21 +97,21 @@ protected JdbcQueryExecution<?> getQueryExecution(JdbcQueryMethod queryMethod,
* @param rowMapper must not be {@literal null}.
* @return a JdbcQueryExecution appropriate for {@literal queryMethod}. Guaranteed to be not {@literal null}.
*/
protected JdbcQueryExecution<?> createReadingQueryExecution(@Nullable ResultSetExtractor<?> extractor,
RowMapper<?> rowMapper) {
JdbcQueryExecution<?> createReadingQueryExecution(@Nullable ResultSetExtractor<?> extractor,
Supplier<RowMapper<?>> rowMapper) {

if (getQueryMethod().isCollectionQuery()) {
return extractor != null ? createSingleReadingQueryExecution(extractor) : collectionQuery(rowMapper);
return extractor != null ? createSingleReadingQueryExecution(extractor) : collectionQuery(rowMapper.get());
}

if (getQueryMethod().isStreamQuery()) {
return extractor != null ? createSingleReadingQueryExecution(extractor) : streamQuery(rowMapper);
return extractor != null ? createSingleReadingQueryExecution(extractor) : streamQuery(rowMapper.get());
}

return extractor != null ? createSingleReadingQueryExecution(extractor) : singleObjectQuery(rowMapper);
return extractor != null ? createSingleReadingQueryExecution(extractor) : singleObjectQuery(rowMapper.get());
}

protected JdbcQueryExecution<Object> createModifyingQueryExecutor() {
JdbcQueryExecution<Object> createModifyingQueryExecutor() {

return (query, parameters) -> {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright 2018-2024 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.repository.query;

import java.sql.SQLType;
import java.util.List;

import org.springframework.core.MethodParameter;
import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.data.relational.repository.query.RelationalParameters;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.ParametersSource;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;

/**
* Custom extension of {@link RelationalParameters}.
*
* @author Mark Paluch
* @since 3.2.6
*/
public class JdbcParameters extends RelationalParameters {

/**
* Creates a new {@link JdbcParameters} instance from the given {@link ParametersSource}.
*
* @param parametersSource must not be {@literal null}.
*/
public JdbcParameters(ParametersSource parametersSource) {
super(parametersSource,
methodParameter -> new JdbcParameter(methodParameter, parametersSource.getDomainTypeInformation()));
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private JdbcParameters(List<JdbcParameter> parameters) {
super((List) parameters);
}

@Override
public JdbcParameter getParameter(int index) {
return (JdbcParameter) super.getParameter(index);
}

@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
protected JdbcParameters createFrom(List<RelationalParameter> parameters) {
return new JdbcParameters((List) parameters);
}

/**
* Custom {@link Parameter} implementation.
*
* @author Mark Paluch
* @author Chirag Tailor
*/
public static class JdbcParameter extends RelationalParameter {

private final SQLType sqlType;
private final Lazy<SQLType> actualSqlType;

/**
* Creates a new {@link RelationalParameter}.
*
* @param parameter must not be {@literal null}.
*/
JdbcParameter(MethodParameter parameter, TypeInformation<?> domainType) {
super(parameter, domainType);

TypeInformation<?> typeInformation = getTypeInformation();

sqlType = JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getType()));

actualSqlType = Lazy.of(() -> JdbcUtil
.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getActualType().getType())));
}

public SQLType getSqlType() {
return sqlType;
}

public SQLType getActualSqlType() {
return actualSqlType.get();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
Expand All @@ -59,6 +60,7 @@ public class JdbcQueryMethod extends QueryMethod {
private final Map<Class<? extends Annotation>, Optional<Annotation>> annotationCache;
private final NamedQueries namedQueries;
private @Nullable RelationalEntityMetadata<?> metadata;
private final boolean modifyingQuery;

// TODO: Remove NamedQueries and put it into JdbcQueryLookupStrategy
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
Expand All @@ -70,11 +72,12 @@ public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFac
this.method = method;
this.mappingContext = mappingContext;
this.annotationCache = new ConcurrentReferenceHashMap<>();
this.modifyingQuery = AnnotationUtils.findAnnotation(method, Modifying.class) != null;
}

@Override
protected Parameters<?, ?> createParameters(ParametersSource parametersSource) {
return new RelationalParameters(parametersSource);
return new JdbcParameters(parametersSource);
}

@Override
Expand Down Expand Up @@ -108,8 +111,8 @@ public RelationalEntityMetadata<?> getEntityInformation() {
}

@Override
public RelationalParameters getParameters() {
return (RelationalParameters) super.getParameters();
public JdbcParameters getParameters() {
return (JdbcParameters) super.getParameters();
}

/**
Expand All @@ -124,6 +127,17 @@ String getDeclaredQuery() {
return StringUtils.hasText(annotatedValue) ? annotatedValue : getNamedQuery();
}

String getRequiredQuery() {

String query = getDeclaredQuery();

if (ObjectUtils.isEmpty(query)) {
throw new IllegalStateException(String.format("No query specified on %s", getName()));
}

return query;
}

/**
* Returns the annotated query if it exists.
*
Expand Down Expand Up @@ -210,7 +224,7 @@ String getResultSetExtractorRef() {
*/
@Override
public boolean isModifyingQuery() {
return AnnotationUtils.findAnnotation(method, Modifying.class) != null;
return modifyingQuery;
}

@SuppressWarnings("unchecked")
Expand Down
Loading
Loading