#2 - Add updates to SimpleR2dbcRepository.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.core.function;
|
||||
|
||||
import io.r2dbc.spi.Row;
|
||||
import io.r2dbc.spi.RowMetadata;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Converter for R2DBC.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class MappingR2dbcConverter {
|
||||
|
||||
private final MappingContext<JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext;
|
||||
|
||||
public MappingR2dbcConverter(MappingContext<JdbcPersistentEntity<?>, JdbcPersistentProperty> mappingContext) {
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Map} that maps column names to an {@link Optional} value. Used {@link Optional#empty()} if the
|
||||
* underlying property is {@literal null}.
|
||||
*
|
||||
* @param object must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Optional<Object>> getFieldsToUpdate(Object object) {
|
||||
|
||||
Assert.notNull(object, "Entity object must not be null!");
|
||||
|
||||
Class<?> userClass = ClassUtils.getUserClass(object);
|
||||
JdbcPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(userClass);
|
||||
|
||||
Map<String, Optional<Object>> update = new LinkedHashMap<>();
|
||||
|
||||
PersistentPropertyAccessor propertyAccessor = entity.getPropertyAccessor(object);
|
||||
|
||||
for (JdbcPersistentProperty property : entity) {
|
||||
update.put(property.getColumnName(), Optional.ofNullable(propertyAccessor.getProperty(property)));
|
||||
}
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link java.util.function.Function} that populates the id property of the {@code object} from a
|
||||
* {@link Row}.
|
||||
*
|
||||
* @param object must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> BiFunction<Row, RowMetadata, T> populateIdIfNecessary(T object) {
|
||||
|
||||
Assert.notNull(object, "Entity object must not be null!");
|
||||
|
||||
Class<?> userClass = ClassUtils.getUserClass(object);
|
||||
JdbcPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(userClass);
|
||||
|
||||
return (row, metadata) -> {
|
||||
|
||||
PersistentPropertyAccessor propertyAccessor = entity.getPropertyAccessor(object);
|
||||
JdbcPersistentProperty idProperty = entity.getRequiredIdProperty();
|
||||
|
||||
if (propertyAccessor.getProperty(idProperty) == null) {
|
||||
|
||||
propertyAccessor.setProperty(idProperty, row.get(idProperty.getColumnName(), idProperty.getColumnType()));
|
||||
return (T) propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
return object;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@@ -26,6 +28,8 @@ import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.jdbc.core.function.DatabaseClient;
|
||||
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.repository.reactive.ReactiveCrudRepository;
|
||||
@@ -39,17 +43,22 @@ import org.springframework.util.Assert;
|
||||
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, JdbcPersistentEntity<T> entity) {
|
||||
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;
|
||||
@@ -66,13 +75,57 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
|
||||
|
||||
if (entity.isNew(objectToSave)) {
|
||||
|
||||
// TODO populate Id back to model
|
||||
return databaseClient.insert().into(entity.getType()).using(objectToSave).then().thenReturn(objectToSave);
|
||||
return databaseClient.insert() //
|
||||
.into(entity.getType()) //
|
||||
.using(objectToSave) //
|
||||
.exchange() //
|
||||
.flatMap(it -> it.extract(converter.populateIdIfNecessary(objectToSave)).one());
|
||||
}
|
||||
|
||||
// TODO update
|
||||
// TODO: Extract in some kind of SQL generator
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(objectToSave);
|
||||
Object id = identifierAccessor.getRequiredIdentifier();
|
||||
|
||||
return null;
|
||||
Map<String, Optional<Object>> fields = converter.getFieldsToUpdate(objectToSave);
|
||||
|
||||
String setClause = getSetClause(fields);
|
||||
|
||||
GenericExecuteSpec exec = databaseClient.execute()
|
||||
.sql(String.format("UPDATE %s SET %s WHERE %s = $1", entity.getTableName(), setClause, getIdColumnName())) //
|
||||
.bind(0, id);
|
||||
|
||||
int index = 1;
|
||||
for (Optional<Object> setValue : fields.values()) {
|
||||
|
||||
Object value = setValue.orElse(null);
|
||||
if (value != null) {
|
||||
exec = exec.bind(index++, value);
|
||||
} else {
|
||||
exec = exec.bindNull(index++);
|
||||
}
|
||||
}
|
||||
|
||||
return exec.as(entity.getType()) //
|
||||
.exchange() //
|
||||
.flatMap(FetchSpec::rowsUpdated) //
|
||||
.thenReturn(objectToSave);
|
||||
}
|
||||
|
||||
private static String getSetClause(Map<String, Optional<Object>> fields) {
|
||||
|
||||
StringBuilder setClause = new StringBuilder();
|
||||
|
||||
int index = 2;
|
||||
for (String field : fields.keySet()) {
|
||||
|
||||
if (setClause.length() != 0) {
|
||||
setClause.append(", ");
|
||||
}
|
||||
|
||||
setClause.append(field).append('=').append('$').append(index++);
|
||||
}
|
||||
|
||||
return setClause.toString();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
Reference in New Issue
Block a user