#2 - Add R2dbcRepositoryFactory and simple query subsystem.
This commit is contained in:
@@ -37,9 +37,10 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
public class MappingR2dbcConverter {
|
||||
|
||||
private final MappingContext<JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext;
|
||||
private final MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext;
|
||||
|
||||
public MappingR2dbcConverter(MappingContext<JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext) {
|
||||
public MappingR2dbcConverter(
|
||||
MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext) {
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
@@ -97,4 +98,8 @@ public class MappingR2dbcConverter {
|
||||
return object;
|
||||
};
|
||||
}
|
||||
|
||||
public MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
|
||||
/**
|
||||
* R2DBC specific {@link org.springframework.data.repository.Repository} interface with reactive support.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface R2dbcRepository<T, ID> extends ReactiveCrudRepository<T, ID> {}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient.GenericExecuteSpec;
|
||||
import org.springframework.data.jdbc.core.function.FetchSpec;
|
||||
import org.springframework.data.jdbc.core.function.MappingR2dbcConverter;
|
||||
import org.springframework.data.jdbc.repository.query.R2dbcQueryExecution.ResultProcessingConverter;
|
||||
import org.springframework.data.jdbc.repository.query.R2dbcQueryExecution.ResultProcessingExecution;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for reactive {@link RepositoryQuery} implementations for R2DBC.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public abstract class AbstractR2dbcQuery implements RepositoryQuery {
|
||||
|
||||
private final R2dbcQueryMethod method;
|
||||
private final DatabaseClient databaseClient;
|
||||
private final MappingR2dbcConverter converter;
|
||||
private final EntityInstantiators instantiators;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractR2dbcQuery} from the given {@link R2dbcQueryMethod} and {@link DatabaseClient}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @param converter must not be {@literal null}.
|
||||
*/
|
||||
public AbstractR2dbcQuery(R2dbcQueryMethod method, DatabaseClient databaseClient, MappingR2dbcConverter converter) {
|
||||
|
||||
Assert.notNull(method, "R2dbcQueryMethod must not be null!");
|
||||
Assert.notNull(databaseClient, "DatabaseClient must not be null!");
|
||||
Assert.notNull(converter, "MappingR2dbcConverter must not be null!");
|
||||
|
||||
this.method = method;
|
||||
this.databaseClient = databaseClient;
|
||||
this.converter = converter;
|
||||
this.instantiators = new EntityInstantiators();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod()
|
||||
*/
|
||||
public R2dbcQueryMethod getQueryMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[])
|
||||
*/
|
||||
public Object execute(Object[] parameters) {
|
||||
|
||||
return method.hasReactiveWrapperParameter() ? executeDeferred(parameters)
|
||||
: execute(new JdbcParametersParameterAccessor(method, parameters));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object executeDeferred(Object[] parameters) {
|
||||
|
||||
R2dbcParameterAccessor parameterAccessor = new R2dbcParameterAccessor(method, parameters);
|
||||
|
||||
if (getQueryMethod().isCollectionQuery()) {
|
||||
return Flux.defer(() -> (Publisher<Object>) execute(parameterAccessor));
|
||||
}
|
||||
|
||||
return Mono.defer(() -> (Mono<Object>) execute(parameterAccessor));
|
||||
}
|
||||
|
||||
private Object execute(JdbcParameterAccessor parameterAccessor) {
|
||||
|
||||
// TODO: ConvertingParameterAccessor
|
||||
BindableQuery query = createQuery(parameterAccessor);
|
||||
|
||||
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(parameterAccessor);
|
||||
GenericExecuteSpec boundQuery = query.bind(databaseClient.execute().sql(query));
|
||||
FetchSpec<?> fetchSpec = boundQuery.as(resolveResultType(processor)).fetch();
|
||||
|
||||
String tableName = method.getEntityInformation().getTableName();
|
||||
|
||||
R2dbcQueryExecution execution = getExecution(
|
||||
new ResultProcessingConverter(processor, converter.getMappingContext(), instantiators));
|
||||
|
||||
return execution.execute(fetchSpec, processor.getReturnedType().getDomainType(), tableName);
|
||||
}
|
||||
|
||||
private Class<?> resolveResultType(ResultProcessor resultProcessor) {
|
||||
|
||||
ReturnedType returnedType = resultProcessor.getReturnedType();
|
||||
|
||||
return returnedType.isProjecting() ? returnedType.getDomainType() : returnedType.getReturnedType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the execution instance to use.
|
||||
*
|
||||
* @param resultProcessing must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private R2dbcQueryExecution getExecution(Converter<Object, Object> resultProcessing) {
|
||||
return new ResultProcessingExecution(getExecutionToWrap(), resultProcessing);
|
||||
}
|
||||
|
||||
private R2dbcQueryExecution getExecutionToWrap() {
|
||||
|
||||
if (method.isCollectionQuery()) {
|
||||
return (q, t, c) -> q.all();
|
||||
}
|
||||
|
||||
return (q, t, c) -> q.one();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BindableQuery} instance using the given {@link ParameterAccessor}
|
||||
*
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected abstract BindableQuery createQuery(JdbcParameterAccessor accessor);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.util.function.Supplier;
|
||||
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient.BindSpec;
|
||||
|
||||
/**
|
||||
* Interface declaring a query that supplies SQL and can bind parameters to a {@link BindSpec}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface BindableQuery extends Supplier<String> {
|
||||
|
||||
/**
|
||||
* Bind parameters to the {@link BindSpec query}.
|
||||
*
|
||||
* @param bindSpec must not be {@literal null}.
|
||||
* @return the bound query object.
|
||||
*/
|
||||
<T extends BindSpec<T>> T bind(T bindSpec);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.EntityInstantiator;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.PreferredConstructor;
|
||||
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.SimplePropertyHandler;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Converter} to instantiate DTOs from fully equipped domain objects.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DtoInstantiatingConverter implements Converter<Object, Object> {
|
||||
|
||||
private final Class<?> targetType;
|
||||
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context;
|
||||
private final EntityInstantiator instantiator;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Converter} to instantiate DTOs.
|
||||
*
|
||||
* @param dtoType must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
* @param instantiators must not be {@literal null}.
|
||||
*/
|
||||
public DtoInstantiatingConverter(Class<?> dtoType,
|
||||
MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> context,
|
||||
EntityInstantiators instantiator) {
|
||||
|
||||
Assert.notNull(dtoType, "DTO type must not be null!");
|
||||
Assert.notNull(context, "MappingContext must not be null!");
|
||||
Assert.notNull(instantiator, "EntityInstantiators must not be null!");
|
||||
|
||||
this.targetType = dtoType;
|
||||
this.context = context;
|
||||
this.instantiator = instantiator.getInstantiatorFor(context.getRequiredPersistentEntity(dtoType));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object source) {
|
||||
|
||||
if (targetType.isInterface()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
final PersistentEntity<?, ?> sourceEntity = context.getRequiredPersistentEntity(source.getClass());
|
||||
final PersistentPropertyAccessor sourceAccessor = sourceEntity.getPropertyAccessor(source);
|
||||
final PersistentEntity<?, ?> targetEntity = context.getRequiredPersistentEntity(targetType);
|
||||
final PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = targetEntity
|
||||
.getPersistenceConstructor();
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
Object dto = instantiator.createInstance(targetEntity, new ParameterValueProvider() {
|
||||
|
||||
@Override
|
||||
public Object getParameterValue(Parameter parameter) {
|
||||
return sourceAccessor.getProperty(sourceEntity.getPersistentProperty(parameter.getName()));
|
||||
}
|
||||
});
|
||||
|
||||
final PersistentPropertyAccessor dtoAccessor = targetEntity.getPropertyAccessor(dto);
|
||||
|
||||
targetEntity.doWithProperties(new SimplePropertyHandler() {
|
||||
|
||||
@Override
|
||||
public void doWithPersistentProperty(PersistentProperty<?> property) {
|
||||
|
||||
if (constructor.isConstructorParameter(property)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dtoAccessor.setProperty(property,
|
||||
sourceAccessor.getProperty(sourceEntity.getPersistentProperty(property.getName())));
|
||||
}
|
||||
});
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 org.springframework.data.repository.core.EntityInformation;
|
||||
|
||||
/**
|
||||
* JDBC specific {@link EntityInformation}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface JdbcEntityInformation<T, ID> extends EntityInformation<T, ID> {
|
||||
|
||||
/**
|
||||
* Returns the name of the table the entity shall be persisted to.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getTableName();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.repository.core.EntityMetadata;
|
||||
|
||||
/**
|
||||
* Extension of {@link EntityMetadata} to additionally expose the collection name an entity shall be persisted to.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface JdbcEntityMetadata<T> extends EntityMetadata<T> {
|
||||
|
||||
/**
|
||||
* Returns the name of the table the entity shall be persisted to.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getTableName();
|
||||
|
||||
/**
|
||||
* Returns the {@link JdbcPersistentEntity} that supposed to determine the table to be queried.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
JdbcPersistentEntity<?> getTableEntity();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
/**
|
||||
* JDBC-specific {@link ParameterAccessor}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface JdbcParameterAccessor extends ParameterAccessor {
|
||||
|
||||
/**
|
||||
* Returns the raw parameter values of the underlying query method.
|
||||
*/
|
||||
Object[] getValues();
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.jdbc.repository.query.JdbcParameters.JdbcParameter;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
/**
|
||||
* Custom extension of {@link Parameters}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class JdbcParameters extends Parameters<JdbcParameters, JdbcParameter> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link JdbcParameters} instance from the given {@link Method}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
*/
|
||||
public JdbcParameters(Method method) {
|
||||
super(method);
|
||||
}
|
||||
|
||||
private JdbcParameters(List<JdbcParameter> parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.Parameters#createParameter(org.springframework.core.MethodParameter)
|
||||
*/
|
||||
@Override
|
||||
protected JdbcParameter createParameter(MethodParameter parameter) {
|
||||
return new JdbcParameter(parameter);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
protected JdbcParameters createFrom(List<JdbcParameter> parameters) {
|
||||
return new JdbcParameters(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link Parameter} implementation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class JdbcParameter extends Parameter {
|
||||
|
||||
/**
|
||||
* Creates a new {@link JdbcParameter}.
|
||||
*
|
||||
* @param parameter must not be {@literal null}.
|
||||
*/
|
||||
JdbcParameter(MethodParameter parameter) {
|
||||
super(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
|
||||
/**
|
||||
* JDBC-specific {@link ParametersParameterAccessor}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class JdbcParametersParameterAccessor extends ParametersParameterAccessor implements JdbcParameterAccessor {
|
||||
|
||||
private final List<Object> values;
|
||||
|
||||
/**
|
||||
* Creates a new {@link JdbcParametersParameterAccessor}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
public JdbcParametersParameterAccessor(QueryMethod method, Object[] values) {
|
||||
|
||||
super(method.getParameters(), values);
|
||||
this.values = Arrays.asList(values);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.repository.query.JdbcParameterAccessor#getValues()
|
||||
*/
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
return values.toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoProcessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers;
|
||||
|
||||
/**
|
||||
* Reactive {@link org.springframework.data.repository.query.ParametersParameterAccessor} implementation that subscribes
|
||||
* to reactive parameter wrapper types upon creation. This class performs synchronization when accessing parameters.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class R2dbcParameterAccessor extends JdbcParametersParameterAccessor {
|
||||
|
||||
private final Object[] values;
|
||||
private final List<MonoProcessor<?>> subscriptions;
|
||||
|
||||
/**
|
||||
* Creates a new {@link R2dbcParameterAccessor}.
|
||||
*/
|
||||
public R2dbcParameterAccessor(R2dbcQueryMethod method, Object... values) {
|
||||
|
||||
super(method, values);
|
||||
|
||||
this.values = values;
|
||||
this.subscriptions = new ArrayList<>(values.length);
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
|
||||
Object value = values[i];
|
||||
|
||||
if (value == null || !ReactiveWrappers.supports(value.getClass())) {
|
||||
subscriptions.add(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ReactiveWrappers.isSingleValueType(value.getClass())) {
|
||||
subscriptions.add(ReactiveWrapperConverters.toWrapper(value, Mono.class).toProcessor());
|
||||
} else {
|
||||
subscriptions.add(ReactiveWrapperConverters.toWrapper(value, Flux.class).collectList().toProcessor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.ParametersParameterAccessor#getValue(int)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected <T> T getValue(int index) {
|
||||
|
||||
if (subscriptions.get(index) != null) {
|
||||
return (T) subscriptions.get(index).block();
|
||||
}
|
||||
|
||||
return super.getValue(index);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.repository.query.JdbcParametersParameterAccessor#getValues()
|
||||
*/
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
|
||||
Object[] result = new Object[values.length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = getValue(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.ParametersParameterAccessor#getBindableValue(int)
|
||||
*/
|
||||
public Object getBindableValue(int index) {
|
||||
return getValue(getParameters().getBindableParameter(index).getIndex());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.jdbc.core.function.FetchSpec;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Set of classes to contain query execution strategies. Depending (mostly) on the return type of a
|
||||
* {@link org.springframework.data.repository.query.QueryMethod}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
interface R2dbcQueryExecution {
|
||||
|
||||
Object execute(FetchSpec<?> query, Class<?> type, String tableName);
|
||||
|
||||
/**
|
||||
* An {@link R2dbcQueryExecution} that wraps the results of the given delegate with the given result processing.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
final class ResultProcessingExecution implements R2dbcQueryExecution {
|
||||
|
||||
private final @NonNull R2dbcQueryExecution delegate;
|
||||
private final @NonNull Converter<Object, Object> converter;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.repository.query.R2dbcQueryExecution#execute(org.springframework.data.jdbc.core.function.FetchSpec, java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object execute(FetchSpec<?> query, Class<?> type, String tableName) {
|
||||
return converter.convert(delegate.execute(query, type, tableName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Converter} to post-process all source objects using the given {@link ResultProcessor}.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
final class ResultProcessingConverter implements Converter<Object, Object> {
|
||||
|
||||
private final @NonNull ResultProcessor processor;
|
||||
private final @NonNull MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext;
|
||||
private final @NonNull EntityInstantiators instantiators;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object source) {
|
||||
|
||||
ReturnedType returnedType = processor.getReturnedType();
|
||||
|
||||
if (ClassUtils.isPrimitiveOrWrapper(returnedType.getReturnedType())) {
|
||||
return source;
|
||||
}
|
||||
|
||||
Converter<Object, Object> converter = new DtoInstantiatingConverter(returnedType.getReturnedType(),
|
||||
mappingContext, instantiators);
|
||||
|
||||
return processor.processResult(source, converter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 static org.springframework.data.repository.util.ClassUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.repository.query.JdbcParameters.JdbcParameter;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Reactive specific implementation of {@link QueryMethod}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class R2dbcQueryMethod extends QueryMethod {
|
||||
|
||||
private static final ClassTypeInformation<Page> PAGE_TYPE = ClassTypeInformation.from(Page.class);
|
||||
private static final ClassTypeInformation<Slice> SLICE_TYPE = ClassTypeInformation.from(Slice.class);
|
||||
|
||||
private final Method method;
|
||||
private final MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext;
|
||||
private final Optional<Query> query;
|
||||
|
||||
private @Nullable JdbcEntityMetadata<?> metadata;
|
||||
|
||||
/**
|
||||
* Creates a new {@link R2dbcQueryMethod} from the given {@link Method}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param metadata must not be {@literal null}.
|
||||
* @param projectionFactory must not be {@literal null}.
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
*/
|
||||
public R2dbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory projectionFactory,
|
||||
MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext) {
|
||||
|
||||
super(method, metadata, projectionFactory);
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
|
||||
this.mappingContext = mappingContext;
|
||||
|
||||
if (hasParameterOfType(method, Pageable.class)) {
|
||||
|
||||
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
|
||||
|
||||
boolean multiWrapper = ReactiveWrappers.isMultiValueType(returnType.getType());
|
||||
boolean singleWrapperWithWrappedPageableResult = ReactiveWrappers.isSingleValueType(returnType.getType())
|
||||
&& (PAGE_TYPE.isAssignableFrom(returnType.getRequiredComponentType())
|
||||
|| SLICE_TYPE.isAssignableFrom(returnType.getRequiredComponentType()));
|
||||
|
||||
if (singleWrapperWithWrappedPageableResult) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String.format("'%s.%s' must not use sliced or paged execution. Please use Flux.buffer(size, skip).",
|
||||
ClassUtils.getShortName(method.getDeclaringClass()), method.getName()));
|
||||
}
|
||||
|
||||
if (!multiWrapper) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: %s",
|
||||
method.toString()));
|
||||
}
|
||||
|
||||
if (hasParameterOfType(method, Sort.class)) {
|
||||
throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameter. "
|
||||
+ "Use sorting capabilities on Pageble instead! Offending method: %s", method.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
this.method = method;
|
||||
this.query = Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, Query.class));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryMethod#createParameters(java.lang.reflect.Method)
|
||||
*/
|
||||
@Override
|
||||
protected JdbcParameters createParameters(Method method) {
|
||||
return new JdbcParameters(method);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryMethod#isCollectionQuery()
|
||||
*/
|
||||
@Override
|
||||
public boolean isCollectionQuery() {
|
||||
return !(isPageQuery() || isSliceQuery()) && ReactiveWrappers.isMultiValueType(method.getReturnType());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryMethod#isModifyingQuery()
|
||||
*/
|
||||
@Override
|
||||
public boolean isModifyingQuery() {
|
||||
return super.isModifyingQuery();
|
||||
}
|
||||
|
||||
/*
|
||||
* All reactive query methods are streaming queries.
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryMethod#isStreamQuery()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStreamQuery() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryMethod#getEntityInformation()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public JdbcEntityMetadata<?> getEntityInformation() {
|
||||
|
||||
if (metadata == null) {
|
||||
|
||||
Class<?> returnedObjectType = getReturnedObjectType();
|
||||
Class<?> domainClass = getDomainClass();
|
||||
|
||||
if (ClassUtils.isPrimitiveOrWrapper(returnedObjectType)) {
|
||||
|
||||
this.metadata = new SimpleJdbcEntityMetadata<>((Class<Object>) domainClass,
|
||||
mappingContext.getRequiredPersistentEntity(domainClass));
|
||||
|
||||
} else {
|
||||
|
||||
JdbcPersistentEntity<?> returnedEntity = mappingContext.getPersistentEntity(returnedObjectType);
|
||||
JdbcPersistentEntity<?> managedEntity = mappingContext.getRequiredPersistentEntity(domainClass);
|
||||
returnedEntity = returnedEntity == null || returnedEntity.getType().isInterface() ? managedEntity
|
||||
: returnedEntity;
|
||||
JdbcPersistentEntity<?> tableEntity = domainClass.isAssignableFrom(returnedObjectType) ? returnedEntity
|
||||
: managedEntity;
|
||||
|
||||
this.metadata = new SimpleJdbcEntityMetadata<>((Class<Object>) returnedEntity.getType(), tableEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryMethod#getParameters()
|
||||
*/
|
||||
@Override
|
||||
public JdbcParameters getParameters() {
|
||||
return (JdbcParameters) super.getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given {@link org.springframework.data.repository.query.QueryMethod} receives a reactive parameter
|
||||
* wrapper as one of its parameters.
|
||||
*
|
||||
* @return {@literal true} if the given {@link org.springframework.data.repository.query.QueryMethod} receives a
|
||||
* reactive parameter wrapper as one of its parameters.
|
||||
*/
|
||||
public boolean hasReactiveWrapperParameter() {
|
||||
|
||||
for (JdbcParameter parameter : getParameters()) {
|
||||
if (ReactiveWrapperConverters.supports(parameter.getType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the required query string declared in a {@link Query} annotation or throws {@link IllegalStateException} if
|
||||
* neither the annotation found nor the attribute was specified.
|
||||
*
|
||||
* @return the query string.
|
||||
* @throws IllegalStateException in case query method has no annotated query.
|
||||
*/
|
||||
public String getRequiredAnnotatedQuery() {
|
||||
return this.query.map(Query::value)
|
||||
.orElseThrow(() -> new IllegalStateException("Query method " + this + " has no annotated query"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Query} annotation that is applied to the method or {@literal null} if none available.
|
||||
*
|
||||
* @return the optional query annotation.
|
||||
*/
|
||||
Optional<Query> getQueryAnnotation() {
|
||||
return this.query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the {@link Method} is annotated with {@link Query}.
|
||||
*/
|
||||
public boolean hasAnnotatedQuery() {
|
||||
return getQueryAnnotation().isPresent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 lombok.Getter;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link JdbcEntityMetadata}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class SimpleJdbcEntityMetadata<T> implements JdbcEntityMetadata<T> {
|
||||
|
||||
private final Class<T> type;
|
||||
private final @Getter JdbcPersistentEntity<?> tableEntity;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleJdbcEntityMetadata} using the given type and {@link JdbcPersistentEntity} to use for
|
||||
* table lookups.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param tableEntity must not be {@literal null}.
|
||||
*/
|
||||
SimpleJdbcEntityMetadata(Class<T> type, JdbcPersistentEntity<?> tableEntity) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
Assert.notNull(tableEntity, "Table entity must not be null!");
|
||||
|
||||
this.type = type;
|
||||
this.tableEntity = tableEntity;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.EntityMetadata#getJavaType()
|
||||
*/
|
||||
public Class<T> getJavaType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.repository.query.JdbcEntityMetadata#getTableName()
|
||||
*/
|
||||
public String getTableName() {
|
||||
return tableEntity.getTableName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 org.springframework.data.jdbc.core.function.DatabaseClient;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient.BindSpec;
|
||||
import org.springframework.data.jdbc.core.function.MappingR2dbcConverter;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* String-based {@link StringBasedR2dbcQuery} implementation.
|
||||
* <p>
|
||||
* A {@link StringBasedR2dbcQuery} expects a query method to be annotated with {@link Query} with a SQL query.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
|
||||
|
||||
private final String sql;
|
||||
|
||||
/**
|
||||
* Creates a new {@link StringBasedR2dbcQuery} for the given {@link StringBasedR2dbcQuery}, {@link DatabaseClient},
|
||||
* {@link SpelExpressionParser}, and {@link QueryMethodEvaluationContextProvider}.
|
||||
*
|
||||
* @param queryMethod must not be {@literal null}.
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @param converter must not be {@literal null}.
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public StringBasedR2dbcQuery(R2dbcQueryMethod queryMethod, DatabaseClient databaseClient,
|
||||
MappingR2dbcConverter converter, SpelExpressionParser expressionParser,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
this(queryMethod.getRequiredAnnotatedQuery(), queryMethod, databaseClient, converter, expressionParser,
|
||||
evaluationContextProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link StringBasedR2dbcQuery} for the given {@code query}, {@link R2dbcQueryMethod},
|
||||
* {@link DatabaseClient}, {@link SpelExpressionParser}, and {@link QueryMethodEvaluationContextProvider}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @param converter must not be {@literal null}.
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public StringBasedR2dbcQuery(String query, R2dbcQueryMethod method, DatabaseClient databaseClient,
|
||||
MappingR2dbcConverter converter, SpelExpressionParser expressionParser,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, databaseClient, converter);
|
||||
|
||||
Assert.hasText(query, "Query must not be empty");
|
||||
|
||||
this.sql = query;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.repository.query.AbstractR2dbcQuery#createQuery(org.springframework.data.jdbc.repository.query.JdbcParameterAccessor)
|
||||
*/
|
||||
@Override
|
||||
protected BindableQuery createQuery(JdbcParameterAccessor accessor) {
|
||||
|
||||
return new BindableQuery() {
|
||||
|
||||
@Override
|
||||
public <T extends BindSpec<T>> T bind(T bindSpec) {
|
||||
|
||||
T bindSpecToUse = bindSpec;
|
||||
|
||||
// TODO: Encapsulate PostgreSQL-specific bindings
|
||||
int index = 1;
|
||||
for (Object value : accessor.getValues()) {
|
||||
|
||||
if (value == null) {
|
||||
if (accessor.hasBindableNullValue()) {
|
||||
bindSpecToUse = bindSpecToUse.bindNull("$" + (index++));
|
||||
}
|
||||
} else {
|
||||
bindSpecToUse = bindSpecToUse.bind("$" + (index++), value);
|
||||
}
|
||||
}
|
||||
|
||||
return bindSpecToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return sql;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.support;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.repository.query.JdbcEntityInformation;
|
||||
import org.springframework.data.repository.core.support.PersistentEntityInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import com.sun.corba.se.spi.ior.ObjectId;
|
||||
|
||||
/**
|
||||
* {@link JdbcEntityInformation} implementation using a {@link JdbcPersistentEntity} instance to lookup the necessary
|
||||
* information. Can be configured with a custom table name.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class MappingJdbcEntityInformation<T, ID> extends PersistentEntityInformation<T, ID>
|
||||
implements JdbcEntityInformation<T, ID> {
|
||||
|
||||
private final JdbcPersistentEntity<T> entityMetadata;
|
||||
private final @Nullable String customTableName;
|
||||
private final Class<ID> fallbackIdType;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingJdbcEntityInformation} for the given {@link JdbcPersistentEntity}.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
*/
|
||||
public MappingJdbcEntityInformation(JdbcPersistentEntity<T> entity) {
|
||||
this(entity, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingJdbcEntityInformation} for the given {@link JdbcPersistentEntity} and fallback
|
||||
* identifier type.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param fallbackIdType can be {@literal null}.
|
||||
*/
|
||||
public MappingJdbcEntityInformation(JdbcPersistentEntity<T> entity, @Nullable Class<ID> fallbackIdType) {
|
||||
this(entity, null, fallbackIdType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingJdbcEntityInformation} for the given {@link JdbcPersistentEntity} and custom table
|
||||
* name.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param customTableName can be {@literal null}.
|
||||
*/
|
||||
public MappingJdbcEntityInformation(JdbcPersistentEntity<T> entity, String customTableName) {
|
||||
this(entity, customTableName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingJdbcEntityInformation} for the given {@link JdbcPersistentEntity}, collection name and
|
||||
* identifier type.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param customTableName can be {@literal null}.
|
||||
* @param idType can be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private MappingJdbcEntityInformation(JdbcPersistentEntity<T> entity, @Nullable String customTableName,
|
||||
@Nullable Class<ID> idType) {
|
||||
|
||||
super(entity);
|
||||
|
||||
this.entityMetadata = entity;
|
||||
this.customTableName = customTableName;
|
||||
this.fallbackIdType = idType != null ? idType : (Class<ID>) ObjectId.class;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.repository.query.JdbcEntityInformation#getTableName()
|
||||
*/
|
||||
public String getTableName() {
|
||||
return customTableName == null ? entityMetadata.getTableName() : customTableName;
|
||||
}
|
||||
|
||||
public String getIdAttribute() {
|
||||
return entityMetadata.getRequiredIdProperty().getName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.PersistentEntityInformation#getIdType()
|
||||
*/
|
||||
@Override
|
||||
public Class<ID> getIdType() {
|
||||
|
||||
if (this.entityMetadata.hasIdProperty()) {
|
||||
return super.getIdType();
|
||||
}
|
||||
|
||||
return fallbackIdType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.support;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient;
|
||||
import org.springframework.data.jdbc.core.function.MappingR2dbcConverter;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.repository.query.JdbcEntityInformation;
|
||||
import org.springframework.data.jdbc.repository.query.R2dbcQueryMethod;
|
||||
import org.springframework.data.jdbc.repository.query.StringBasedR2dbcQuery;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Factory to create {@link org.springframework.data.jdbc.repository.R2dbcRepository} instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class R2dbcRepositoryFactory extends ReactiveRepositoryFactorySupport {
|
||||
|
||||
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
|
||||
private final DatabaseClient databaseClient;
|
||||
private final MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext;
|
||||
private final MappingR2dbcConverter converter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link R2dbcRepositoryFactory} given {@link DatabaseClient} and {@link MappingContext}.
|
||||
*
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
*/
|
||||
public R2dbcRepositoryFactory(DatabaseClient databaseClient,
|
||||
MappingContext<? extends JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext) {
|
||||
|
||||
Assert.notNull(databaseClient, "DatabaseClient must not be null!");
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
|
||||
this.databaseClient = databaseClient;
|
||||
this.mappingContext = mappingContext;
|
||||
this.converter = new MappingR2dbcConverter(mappingContext);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
|
||||
return SimpleR2dbcRepository.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryInformation)
|
||||
*/
|
||||
@Override
|
||||
protected Object getTargetRepository(RepositoryInformation information) {
|
||||
|
||||
JdbcEntityInformation<?, ?> entityInformation = getEntityInformation(information.getDomainType(), information);
|
||||
|
||||
return getTargetRepositoryViaReflection(information, entityInformation, databaseClient, converter);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider)
|
||||
*/
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
return Optional.of(new R2dbcQueryLookupStrategy(databaseClient, evaluationContextProvider, converter));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
|
||||
*/
|
||||
public <T, ID> JdbcEntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
|
||||
return getEntityInformation(domainClass, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T, ID> JdbcEntityInformation<T, ID> getEntityInformation(Class<T> domainClass,
|
||||
@Nullable RepositoryInformation information) {
|
||||
|
||||
JdbcPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(domainClass);
|
||||
|
||||
return new MappingJdbcEntityInformation<>((JdbcPersistentEntity<T>) entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link QueryLookupStrategy} to create R2DBC queries..
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
private static class R2dbcQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final DatabaseClient databaseClient;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final MappingR2dbcConverter converter;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
|
||||
*/
|
||||
@Override
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
|
||||
NamedQueries namedQueries) {
|
||||
|
||||
R2dbcQueryMethod queryMethod = new R2dbcQueryMethod(method, metadata, factory, converter.getMappingContext());
|
||||
String namedQueryName = queryMethod.getNamedQueryName();
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
return new StringBasedR2dbcQuery(namedQuery, queryMethod, databaseClient, converter, EXPRESSION_PARSER,
|
||||
evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new StringBasedR2dbcQuery(queryMethod, databaseClient, converter, EXPRESSION_PARSER,
|
||||
evaluationContextProvider);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Query derivation not yet supported!");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -30,8 +32,7 @@ import org.springframework.data.jdbc.core.function.DatabaseClient.BindSpec;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient.GenericExecuteSpec;
|
||||
import org.springframework.data.jdbc.core.function.FetchSpec;
|
||||
import org.springframework.data.jdbc.core.function.MappingR2dbcConverter;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.mapping.IdentifierAccessor;
|
||||
import org.springframework.data.jdbc.repository.query.JdbcEntityInformation;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -40,30 +41,12 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, ID> {
|
||||
|
||||
private final DatabaseClient databaseClient;
|
||||
private final MappingR2dbcConverter converter;
|
||||
private final JdbcPersistentEntity<T> entity;
|
||||
|
||||
/**
|
||||
* Create a new {@link SimpleR2dbcRepository} given {@link DatabaseClient} and {@link JdbcPersistentEntity}.
|
||||
*
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @param converter must not be {@literal null}.
|
||||
* @param entity must not be {@literal null}.
|
||||
*/
|
||||
public SimpleR2dbcRepository(DatabaseClient databaseClient, MappingR2dbcConverter converter,
|
||||
JdbcPersistentEntity<T> entity) {
|
||||
this.converter = converter;
|
||||
|
||||
Assert.notNull(databaseClient, "DatabaseClient must not be null!");
|
||||
Assert.notNull(converter, "MappingR2dbcConverter must not be null!");
|
||||
Assert.notNull(entity, "PersistentEntity must not be null!");
|
||||
|
||||
this.databaseClient = databaseClient;
|
||||
this.entity = entity;
|
||||
}
|
||||
private final @NonNull JdbcEntityInformation<T, ID> entity;
|
||||
private final @NonNull DatabaseClient databaseClient;
|
||||
private final @NonNull MappingR2dbcConverter converter;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.reactive.ReactiveCrudRepository#save(S)
|
||||
@@ -76,15 +59,14 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
if (entity.isNew(objectToSave)) {
|
||||
|
||||
return databaseClient.insert() //
|
||||
.into(entity.getType()) //
|
||||
.into(entity.getJavaType()) //
|
||||
.using(objectToSave) //
|
||||
.exchange() //
|
||||
.flatMap(it -> it.extract(converter.populateIdIfNecessary(objectToSave)).one());
|
||||
}
|
||||
|
||||
// TODO: Extract in some kind of SQL generator
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(objectToSave);
|
||||
Object id = identifierAccessor.getRequiredIdentifier();
|
||||
Object id = entity.getRequiredId(objectToSave);
|
||||
|
||||
Map<String, Optional<Object>> fields = converter.getFieldsToUpdate(objectToSave);
|
||||
|
||||
@@ -105,7 +87,7 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
}
|
||||
}
|
||||
|
||||
return exec.as(entity.getType()) //
|
||||
return exec.as(entity.getJavaType()) //
|
||||
.exchange() //
|
||||
.flatMap(FetchSpec::rowsUpdated) //
|
||||
.thenReturn(objectToSave);
|
||||
@@ -162,7 +144,7 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
return databaseClient.execute()
|
||||
.sql(String.format("SELECT * FROM %s WHERE %s = $1", entity.getTableName(), getIdColumnName())) //
|
||||
.bind("$1", id) //
|
||||
.as(entity.getType()) //
|
||||
.as(entity.getJavaType()) //
|
||||
.fetch() //
|
||||
.one();
|
||||
|
||||
@@ -206,7 +188,7 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
*/
|
||||
@Override
|
||||
public Flux<T> findAll() {
|
||||
return databaseClient.select().from(entity.getType()).fetch().all();
|
||||
return databaseClient.select().from(entity.getJavaType()).fetch().all();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -235,7 +217,7 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
GenericExecuteSpec exec = databaseClient.execute()
|
||||
.sql(String.format("SELECT * FROM %s WHERE %s IN (%s)", entity.getTableName(), getIdColumnName(), bindings));
|
||||
|
||||
return bind(ids, exec).as(entity.getType()).fetch().all();
|
||||
return bind(ids, exec).as(entity.getJavaType()).fetch().all();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -284,7 +266,7 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
GenericExecuteSpec exec = databaseClient.execute()
|
||||
.sql(String.format("DELETE FROM %s WHERE %s IN (%s)", entity.getTableName(), getIdColumnName(), bindings));
|
||||
|
||||
return bind(ids, exec).as(entity.getType()).fetch().rowsUpdated();
|
||||
return bind(ids, exec).as(entity.getJavaType()).fetch().rowsUpdated();
|
||||
}).then();
|
||||
}
|
||||
|
||||
@@ -297,9 +279,7 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
|
||||
Assert.notNull(objectToDelete, "Object to delete must not be null!");
|
||||
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(objectToDelete);
|
||||
|
||||
return deleteById((ID) identifierAccessor.getRequiredIdentifier());
|
||||
return deleteById(entity.getRequiredId(objectToDelete));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -323,8 +303,7 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
Assert.notNull(objectPublisher, "The Object Publisher must not be null!");
|
||||
|
||||
Flux<ID> idPublisher = Flux.from(objectPublisher) //
|
||||
.map(entity::getIdentifierAccessor) //
|
||||
.map(identifierAccessor -> (ID) identifierAccessor.getRequiredIdentifier());
|
||||
.map(entity::getRequiredId);
|
||||
|
||||
return deleteById(idPublisher);
|
||||
}
|
||||
@@ -355,6 +334,7 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
}
|
||||
|
||||
private String getIdColumnName() {
|
||||
return entity.getRequiredIdProperty().getColumnName();
|
||||
return converter.getMappingContext().getRequiredPersistentEntity(entity.getJavaType()).getRequiredIdProperty()
|
||||
.getColumnName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2017-2018 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
|
||||
*
|
||||
* http://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.degraph;
|
||||
|
||||
import static de.schauderhaft.degraph.check.JCheck.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import de.schauderhaft.degraph.check.JCheck;
|
||||
import scala.runtime.AbstractFunction1;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test package dependencies for violations.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class DependencyTests {
|
||||
|
||||
@Test // DATAJDBC-114
|
||||
public void cycleFree() {
|
||||
|
||||
assertThat( //
|
||||
classpath() //
|
||||
.noJars() //
|
||||
.including("org.springframework.data.jdbc.**") //
|
||||
.filterClasspath("*target/classes") // exclude test code
|
||||
.printOnFailure("degraph.graphml"),
|
||||
JCheck.violationFree());
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-220
|
||||
@Ignore("I don't understand why this fails after adding reactive repos - mp911de")
|
||||
public void acrossModules() {
|
||||
|
||||
assertThat( //
|
||||
classpath() //
|
||||
// include only Spring Data related classes (for example no JDK code)
|
||||
.including("org.springframework.data.**") //
|
||||
.filterClasspath(new AbstractFunction1<String, Object>() {
|
||||
@Override
|
||||
public Object apply(String s) { //
|
||||
// only the current module + commons
|
||||
return s.endsWith("target/classes") || s.contains("spring-data-commons");
|
||||
}
|
||||
}) // exclude test code
|
||||
.withSlicing("sub-modules", // sub-modules are defined by any of the following pattern.
|
||||
"org.springframework.data.jdbc.(**).*", //
|
||||
"org.springframework.data.(**).*") //
|
||||
.printTo("degraph-across-modules.graphml"), // writes a graphml to this location
|
||||
JCheck.violationFree());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Hooks;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient;
|
||||
import org.springframework.data.jdbc.core.function.DefaultReactiveDataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.core.mapping.Table;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.jdbc.repository.support.R2dbcRepositoryFactory;
|
||||
import org.springframework.data.jdbc.testing.R2dbcIntegrationTestSupport;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link LegoSetRepository} using {@link R2dbcRepositoryFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class R2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
|
||||
private static JdbcMappingContext mappingContext = new JdbcMappingContext();
|
||||
|
||||
private ConnectionFactory connectionFactory;
|
||||
private DatabaseClient databaseClient;
|
||||
private LegoSetRepository repository;
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
Hooks.onOperatorDebug();
|
||||
|
||||
this.connectionFactory = createConnectionFactory();
|
||||
this.databaseClient = DatabaseClient.builder().connectionFactory(connectionFactory)
|
||||
.dataAccessStrategy(new DefaultReactiveDataAccessStrategy(mappingContext, new EntityInstantiators())).build();
|
||||
|
||||
this.jdbc = createJdbcTemplate(createDataSource());
|
||||
|
||||
String tableToCreate = "CREATE TABLE IF NOT EXISTS repo_legoset (\n" + " id SERIAL PRIMARY KEY,\n"
|
||||
+ " name varchar(255) NOT NULL,\n" + " manual integer NULL\n" + ");";
|
||||
|
||||
this.jdbc.execute("DROP TABLE IF EXISTS repo_legoset");
|
||||
this.jdbc.execute(tableToCreate);
|
||||
|
||||
this.repository = new R2dbcRepositoryFactory(databaseClient, mappingContext).getRepository(LegoSetRepository.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldInsertNewItems() {
|
||||
|
||||
LegoSet legoSet1 = new LegoSet(null, "SCHAUFELRADBAGGER", 12);
|
||||
LegoSet legoSet2 = new LegoSet(null, "FORSCHUNGSSCHIFF", 13);
|
||||
|
||||
repository.saveAll(Arrays.asList(legoSet1, legoSet2)) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextCount(2) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindItemsByManual() {
|
||||
|
||||
shouldInsertNewItems();
|
||||
|
||||
repository.findByManual(13) //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(actual -> {
|
||||
assertThat(actual.getName()).isEqualTo("FORSCHUNGSSCHIFF");
|
||||
}) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindItemsByNameLike() {
|
||||
|
||||
shouldInsertNewItems();
|
||||
|
||||
repository.findByNameContains("%F%") //
|
||||
.map(LegoSet::getName) //
|
||||
.collectList() //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(actual -> {
|
||||
assertThat(actual).contains("SCHAUFELRADBAGGER", "FORSCHUNGSSCHIFF");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindApplyingProjection() {
|
||||
|
||||
shouldInsertNewItems();
|
||||
|
||||
repository.findAsProjection() //
|
||||
.map(Named::getName) //
|
||||
.collectList() //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(actual -> {
|
||||
assertThat(actual).contains("SCHAUFELRADBAGGER", "FORSCHUNGSSCHIFF");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
interface LegoSetRepository extends ReactiveCrudRepository<LegoSet, Integer> {
|
||||
|
||||
@Query("SELECT * FROM repo_legoset WHERE name like $1")
|
||||
Flux<LegoSet> findByNameContains(String name);
|
||||
|
||||
@Query("SELECT * FROM repo_legoset")
|
||||
Flux<Named> findAsProjection();
|
||||
|
||||
@Query("SELECT * FROM repo_legoset WHERE manual = $1")
|
||||
Mono<LegoSet> findByManual(int manual);
|
||||
}
|
||||
|
||||
@Data
|
||||
@Table("repo_legoset")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
static class LegoSet {
|
||||
@Id Integer id;
|
||||
String name;
|
||||
Integer manual;
|
||||
}
|
||||
|
||||
interface Named {
|
||||
String getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
/**
|
||||
* Unit test for {@link R2dbcQueryMethod}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class R2dbcQueryMethodUnitTests {
|
||||
|
||||
JdbcMappingContext context;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = new JdbcMappingContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectsCollectionFromReturnTypeIfReturnTypeAssignable() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = queryMethod(SampleRepository.class, "method");
|
||||
JdbcEntityMetadata<?> metadata = queryMethod.getEntityInformation();
|
||||
|
||||
assertThat(metadata.getJavaType()).isAssignableFrom(Contact.class);
|
||||
assertThat(metadata.getTableName()).isEqualTo("contact");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectsTableNameFromRepoTypeIfReturnTypeNotAssignable() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = queryMethod(SampleRepository.class, "differentTable");
|
||||
JdbcEntityMetadata<?> metadata = queryMethod.getEntityInformation();
|
||||
|
||||
assertThat(metadata.getJavaType()).isAssignableFrom(Address.class);
|
||||
assertThat(metadata.getTableName()).isEqualTo("contact");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullMappingContext() throws Exception {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findMonoByLastname", String.class, Pageable.class);
|
||||
|
||||
new R2dbcQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class),
|
||||
new SpelAwareProxyProjectionFactory(), null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void rejectsMonoPageableResult() throws Exception {
|
||||
queryMethod(PersonRepository.class, "findMonoByLastname", String.class, Pageable.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsQueryMethodObjectForMethodReturningAnInterface() throws Exception {
|
||||
queryMethod(SampleRepository.class, "methodReturningAnInterface");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
public void throwsExceptionOnWrappedPage() throws Exception {
|
||||
queryMethod(PersonRepository.class, "findMonoPageByLastname", String.class, Pageable.class);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
public void throwsExceptionOnWrappedSlice() throws Exception {
|
||||
queryMethod(PersonRepository.class, "findMonoSliceByLastname", String.class, Pageable.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fallsBackToRepositoryDomainTypeIfMethodDoesNotReturnADomainType() throws Exception {
|
||||
|
||||
R2dbcQueryMethod method = queryMethod(PersonRepository.class, "deleteByUserName", String.class);
|
||||
|
||||
assertThat(method.getEntityInformation().getJavaType()).isAssignableFrom(Contact.class);
|
||||
}
|
||||
|
||||
private R2dbcQueryMethod queryMethod(Class<?> repository, String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
Method method = repository.getMethod(name, parameters);
|
||||
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||
return new R2dbcQueryMethod(method, new DefaultRepositoryMetadata(repository), factory, context);
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<Contact, Long> {
|
||||
|
||||
Mono<Contact> findMonoByLastname(String lastname, Pageable pageRequest);
|
||||
|
||||
Mono<Page<Contact>> findMonoPageByLastname(String lastname, Pageable pageRequest);
|
||||
|
||||
Mono<Slice<Contact>> findMonoSliceByLastname(String lastname, Pageable pageRequest);
|
||||
|
||||
void deleteByUserName(String userName);
|
||||
}
|
||||
|
||||
interface SampleRepository extends Repository<Contact, Long> {
|
||||
|
||||
List<Contact> method();
|
||||
|
||||
List<Address> differentTable();
|
||||
|
||||
Customer methodReturningAnInterface();
|
||||
}
|
||||
|
||||
interface Customer {}
|
||||
|
||||
static class Contact {}
|
||||
|
||||
static class Address {}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient.GenericExecuteSpec;
|
||||
import org.springframework.data.jdbc.core.function.MappingR2dbcConverter;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link StringBasedR2dbcQuery}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StringBasedR2dbcQueryUnitTests {
|
||||
|
||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
@Mock private DatabaseClient databaseClient;
|
||||
@Mock private GenericExecuteSpec bindSpec;
|
||||
|
||||
private JdbcMappingContext mappingContext;
|
||||
private MappingR2dbcConverter converter;
|
||||
private ProjectionFactory factory;
|
||||
private RepositoryMetadata metadata;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setUp() {
|
||||
|
||||
this.mappingContext = new JdbcMappingContext();
|
||||
this.converter = new MappingR2dbcConverter(this.mappingContext);
|
||||
this.metadata = AbstractRepositoryMetadata.getMetadata(SampleRepository.class);
|
||||
this.factory = new SpelAwareProxyProjectionFactory();
|
||||
|
||||
when(bindSpec.bind(anyString(), any())).thenReturn(bindSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindsSimplePropertyCorrectly() {
|
||||
|
||||
StringBasedR2dbcQuery query = getQueryMethod("findByLastname", String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor);
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
|
||||
verify(bindSpec).bind("$1", "White");
|
||||
}
|
||||
|
||||
private StringBasedR2dbcQuery getQueryMethod(String name, Class<?>... args) {
|
||||
|
||||
Method method = ReflectionUtils.findMethod(SampleRepository.class, name, args);
|
||||
|
||||
R2dbcQueryMethod queryMethod = new R2dbcQueryMethod(method, metadata, factory, converter.getMappingContext());
|
||||
|
||||
return new StringBasedR2dbcQuery(queryMethod, databaseClient, converter, PARSER,
|
||||
ExtensionAwareQueryMethodEvaluationContextProvider.DEFAULT);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private interface SampleRepository extends Repository<Person, String> {
|
||||
|
||||
@Query("SELECT * FROM person WHERE lastname = $1")
|
||||
Person findByLastname(String lastname);
|
||||
}
|
||||
|
||||
static class Person {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.repository.query.JdbcEntityInformation;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.Repository;
|
||||
|
||||
/**
|
||||
* Unit test for {@link R2dbcRepositoryFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class R2dbcRepositoryFactoryUnitTests {
|
||||
|
||||
@Mock DatabaseClient databaseClient;
|
||||
@Mock @SuppressWarnings("rawtypes") MappingContext mappingContext;
|
||||
@Mock @SuppressWarnings("rawtypes") JdbcPersistentEntity entity;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void before() {
|
||||
when(mappingContext.getRequiredPersistentEntity(Person.class)).thenReturn(entity);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void usesMappingJdbcEntityInformationIfMappingContextSet() {
|
||||
|
||||
R2dbcRepositoryFactory factory = new R2dbcRepositoryFactory(databaseClient, mappingContext);
|
||||
JdbcEntityInformation<Person, Long> entityInformation = factory.getEntityInformation(Person.class);
|
||||
|
||||
assertThat(entityInformation).isInstanceOf(MappingJdbcEntityInformation.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void createsRepositoryWithIdTypeLong() {
|
||||
|
||||
R2dbcRepositoryFactory factory = new R2dbcRepositoryFactory(databaseClient, mappingContext);
|
||||
MyPersonRepository repository = factory.getRepository(MyPersonRepository.class);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
}
|
||||
|
||||
interface MyPersonRepository extends Repository<Person, Long> {}
|
||||
|
||||
static class Person {}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import org.springframework.data.jdbc.core.function.MappingR2dbcConverter;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.mapping.Table;
|
||||
import org.springframework.data.jdbc.repository.query.JdbcEntityInformation;
|
||||
import org.springframework.data.jdbc.testing.R2dbcIntegrationTestSupport;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
@@ -65,10 +66,13 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
this.connectionFactory = createConnectionFactory();
|
||||
this.databaseClient = DatabaseClient.builder().connectionFactory(connectionFactory)
|
||||
.dataAccessStrategy(new DefaultReactiveDataAccessStrategy(mappingContext, new EntityInstantiators())).build();
|
||||
this.repository = new SimpleR2dbcRepository<>(databaseClient,
|
||||
new MappingR2dbcConverter(mappingContext),
|
||||
|
||||
JdbcEntityInformation<LegoSet, Integer> entityInformation = new MappingJdbcEntityInformation<>(
|
||||
(JdbcPersistentEntity<LegoSet>) mappingContext.getRequiredPersistentEntity(LegoSet.class));
|
||||
|
||||
this.repository = new SimpleR2dbcRepository<>(entityInformation, databaseClient,
|
||||
new MappingR2dbcConverter(mappingContext));
|
||||
|
||||
this.jdbc = createJdbcTemplate(createDataSource());
|
||||
|
||||
String tableToCreate = "CREATE TABLE IF NOT EXISTS repo_legoset (\n" + " id SERIAL PRIMARY KEY,\n"
|
||||
|
||||
Reference in New Issue
Block a user