Reorganize repository so additional modules can be added. (#1514)

Closes #1503.
This commit is contained in:
Michael Reiche
2022-07-28 15:26:53 -07:00
committed by GitHub
parent 28d9f8f27b
commit a998251585
449 changed files with 485 additions and 288 deletions

View File

@@ -1,34 +0,0 @@
/*
/*
* Copyright 2021-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.couchbase.client.java.transactions;
import com.couchbase.client.core.annotation.Stability;
import com.couchbase.client.core.transaction.CoreTransactionAttemptContext;
import com.couchbase.client.java.codec.JsonSerializer;
/**
* To access the ReactiveTransactionAttemptContext held by TransactionAttemptContext
*
* @author Michael Reiche
*/
@Stability.Internal
public class AttemptContextReactiveAccessor {
public static ReactiveTransactionAttemptContext createReactiveTransactionAttemptContext(
CoreTransactionAttemptContext core, JsonSerializer jsonSerializer) {
return new ReactiveTransactionAttemptContext(core, jsonSerializer);
}
}

View File

@@ -1,215 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.querydsl.couchbase.document;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
import com.querydsl.core.DefaultQueryMetadata;
import com.querydsl.core.JoinExpression;
import com.querydsl.core.QueryMetadata;
import com.querydsl.core.QueryModifiers;
import com.querydsl.core.SimpleQuery;
import com.querydsl.core.support.QueryMixin;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.FactoryExpression;
import com.querydsl.core.types.Operation;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.ParamExpression;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
/**
* renamed from AbstractCouchbaseQuery to AbstractCouchbaseQueryDSL to avoid confusion with the AbstractCouchbaseQuery
* that is in the package com.querydsl.couchbase
*
* @author Michael Reiche
*/
public abstract class AbstractCouchbaseQueryDSL<Q extends AbstractCouchbaseQueryDSL<Q>> implements SimpleQuery<Q> {
private final CouchbaseDocumentSerializer serializer;
private final QueryMixin<Q> queryMixin;// = new QueryMixin(this, new DefaultQueryMetadata(), false);
// TODO private ReadPreference readPreference;
public AbstractCouchbaseQueryDSL(CouchbaseDocumentSerializer serializer) {
this.serializer = serializer;
@SuppressWarnings("unchecked") // Q is this plus subclass
Q query = (Q) this;
this.queryMixin = new QueryMixin<Q>(query, new DefaultQueryMetadata(), false);
}
/**
* other spring-data project uses createQuery(Predicate filter) where the serializer creates the 'query' <br>
* and then uses the result to create a BasicQuery with queryObject = result <br>
* Couchbase Query has a 'criteria' which is a <br>
* List&lt;QueryCriteriaDefinition&gt; criteria <br>
* so we could create a List&lt;QueryCriteriaDefinition&gt; or an uber QueryCriteria that combines <br>
* all the sub QueryDefinitions in the filter.
*/
protected QueryCriteriaDefinition createCriteria(Predicate predicate) {
// other project use createQuery(Predicate filter) where the serializer creates the 'queryObject' of the BasicQuery
return predicate != null ? (QueryCriteriaDefinition) this.serializer.handle(predicate) : null;
}
// TODO - need later
// public <T> JoinBuilder<Q, T> join(Path<T> ref, Path<T> target) {
// return new JoinBuilder(this.queryMixin, ref, target);
// }
// public <T> JoinBuilder<Q, T> join(CollectionPathBase<?, T, ?> ref, Path<T> target) {
// return new JoinBuilder(this.queryMixin, ref, target);
// }
// public <T> AnyEmbeddedBuilder<Q> anyEmbedded(Path<? extends Collection<T>> collection, Path<T> target) {
// return new AnyEmbeddedBuilder(this.queryMixin, collection);
// }
@Nullable
protected Predicate createFilter(QueryMetadata metadata) {
Predicate filter;
if (!metadata.getJoins().isEmpty()) {
filter = ExpressionUtils.allOf(new Predicate[] { metadata.getWhere(), this.createJoinFilter(metadata) });
} else {
filter = metadata.getWhere();
}
return filter;
}
@Nullable
protected Predicate createJoinFilter(QueryMetadata metadata) {
Map<Expression<?>, Predicate> predicates = new HashMap();
List<JoinExpression> joins = metadata.getJoins();
for (int i = joins.size() - 1; i >= 0; --i) {
JoinExpression join = (JoinExpression) joins.get(i);
Path<?> source = (Path) ((Operation) join.getTarget()).getArg(0);
Path<?> target = (Path) ((Operation) join.getTarget()).getArg(1);
Predicate extraFilters = (Predicate) predicates.get(target.getRoot());
Predicate filter = ExpressionUtils.allOf(new Predicate[] { join.getCondition(), extraFilters });
List<? extends Object> ids = this.getIds(target.getType(), filter);
if (ids.isEmpty()) {
throw new AbstractCouchbaseQueryDSL.NoResults();
}
Path<?> path = ExpressionUtils.path(String.class, source, "$id");
predicates.merge(source.getRoot(),
ExpressionUtils.in(path, (Collection) ids/* TODO was just ids without casting to Collection */),
ExpressionUtils::and);
}
Path<?> source = (Path) ((Operation) ((JoinExpression) joins.get(0)).getTarget()).getArg(0);
return predicates.get(source.getRoot());
}
private Predicate allOf(Collection<Predicate> predicates) {
return predicates != null ? ExpressionUtils.allOf(predicates) : null;
}
protected abstract List<Object> getIds(Class<?> var1, Predicate var2);
public Q distinct() {
return this.queryMixin.distinct();
}
public Q where(Predicate e) {
return this.queryMixin.where(e);
}
public Q where(Predicate... e) {
return this.queryMixin.where(e);
}
public Q limit(long limit) {
return this.queryMixin.limit(limit);
}
public Q offset(long offset) {
return this.queryMixin.offset(offset);
}
public Q restrict(QueryModifiers modifiers) {
return this.queryMixin.restrict(modifiers);
}
public Q orderBy(OrderSpecifier<?> o) {
return this.queryMixin.orderBy(o);
}
public Q orderBy(OrderSpecifier<?>... o) {
return this.queryMixin.orderBy(o);
}
public <T> Q set(ParamExpression<T> param, T value) {
return this.queryMixin.set(param, value);
}
protected Map<String, String> createProjection(Expression<?> projection) {
if (projection instanceof FactoryExpression) {
Map<String, String> obj = new HashMap();
Iterator var3 = ((FactoryExpression) projection).getArgs().iterator();
while (var3.hasNext()) {
Object expr = var3.next();
if (expr instanceof Expression) {
obj.put(expr.toString(), (String) this.serializer.handle((Expression) expr));
}
}
return obj;
} else {
return null;
}
}
protected CouchbaseDocument createQuery(@Nullable Predicate predicate) {
return predicate != null ? (CouchbaseDocument) this.serializer.handle(predicate) : new CouchbaseDocument();
}
// public void setReadPreference(ReadPreference readPreference) {
// this.readPreference = readPreference;
// }
protected QueryMixin<Q> getQueryMixin() {
return this.queryMixin;
}
protected CouchbaseDocumentSerializer getSerializer() {
return this.serializer;
}
// protected ReadPreference getReadPreference() {
// return this.readPreference;
// }
public CouchbaseDocument asDocument() {
return this.createQuery(this.queryMixin.getMetadata().getWhere());
}
public String toString() {
return this.asDocument().toString();
}
static class NoResults extends RuntimeException {
NoResults() {}
}
}

View File

@@ -1,403 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.querydsl.couchbase.document;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
import org.springframework.data.couchbase.core.query.QueryCriteria;
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
import org.springframework.data.couchbase.repository.support.DBRef;
import org.springframework.data.domain.Sort;
import com.querydsl.core.types.Constant;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.FactoryExpression;
import com.querydsl.core.types.Operation;
import com.querydsl.core.types.Operator;
import com.querydsl.core.types.Ops;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.ParamExpression;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathMetadata;
import com.querydsl.core.types.PathType;
import com.querydsl.core.types.SubQueryExpression;
import com.querydsl.core.types.TemplateExpression;
import com.querydsl.core.types.Visitor;
/**
* Serializes the given Querydsl query to a Document query for Couchbase.
*
* @author Michael Reiche
*/
public abstract class CouchbaseDocumentSerializer implements Visitor<Object, Void> {
public Object handle(Expression<?> expression) {
return expression.accept(this, null);
}
public Sort toSort(List<OrderSpecifier<?>> orderBys) {
Sort sort = Sort.unsorted();
for (OrderSpecifier<?> orderBy : orderBys) {
Object key = orderBy.getTarget().accept(this, null);
// sort.and(Sort.by(orderBy));
// sort.append(key.toString(), orderBy.getOrder() == Order.ASC ? 1 : -1);
}
return sort;
}
@Override
public Object visit(Constant<?> expr, Void context) {
if (Enum.class.isAssignableFrom(expr.getType())) {
@SuppressWarnings("unchecked") // Guarded by previous check
Constant<? extends Enum<?>> expectedExpr = (Constant<? extends Enum<?>>) expr;
return expectedExpr.getConstant().name();
} else {
return expr.getConstant();
}
}
@Override
public Object visit(TemplateExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(FactoryExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
protected String asDBKey(Operation<?> expr, int index) {
return (String) asDBValue(expr, index);
}
protected Object asDBValue(Operation<?> expr, int index) {
return expr.getArg(index).accept(this, null);
}
private String regexValue(Operation<?> expr, int index) {
return Pattern.quote(expr.getArg(index).accept(this, null).toString());
}
protected QueryCriteriaDefinition asDocument(String key, Object value) {
QueryCriteria qc = null;
if (1 == 1) {
throw new UnsupportedOperationException("Wrong path to create this criteria " + key);
}
if (key.equals("$and") || key.equals("$or") /* value instanceof QueryCriteria[] */) {
throw new UnsupportedOperationException("Wrong path to create this criteria " + key);
} else if (key.equals("$in") /* value instanceof QueryCriteria[] */) {
throw new RuntimeException(("not supported"));
} else {
qc = QueryCriteria.where(key).is(value);
}
return qc;
}
@SuppressWarnings("unchecked")
@Override
public Object visit(Operation<?> expr, Void context) {
Operator op = expr.getOperator();
if (op == Ops.EQ) {
if (expr.getArg(0) instanceof Operation) {
Operation<?> lhs = (Operation<?>) expr.getArg(0);
if (lhs.getOperator() == Ops.COL_SIZE || lhs.getOperator() == Ops.ARRAY_SIZE
|| lhs.getOperator() == Ops.STRING_LENGTH) {
// return asDocument(asDBKey(lhs, 0), asDocument("$size", asDBValue(expr, 1)));
return QueryCriteria.where(asDBKey(expr, 0)).is(asDBValue(expr, 1));
} else {
throw new UnsupportedOperationException("Illegal operation " + expr);
}
} else if (expr.getArg(0) instanceof Path) {
/*
Path<?> path = (Path<?>) expr.getArg(0);
Constant<?> constant = (Constant<?>) expr.getArg(1);
return asDocument(asDBKey(expr, 0), convert(path, constant));
*/
return QueryCriteria.where(asDBKey(expr, 0)).is(asDBValue(expr, 1));
}
} else if (op == Ops.STRING_IS_EMPTY) {
// return asDocument(asDBKey(expr, 0), "");
return QueryCriteria.where(asDBKey(expr, 0)).isNotValued().or(asDBKey(expr, 0)).is("");
} else if (op == Ops.NOT) {
// Handle the not's child
Operation<?> subOperation = (Operation<?>) expr.getArg(0);
Operator subOp = subOperation.getOperator();
if (subOp == Ops.IN) {
return visit(
ExpressionUtils.operation(Boolean.class, Ops.NOT_IN, subOperation.getArg(0), subOperation.getArg(1)),
context);
} else {
QueryCriteria arg = (QueryCriteria) handle(expr.getArg(0));
return arg.negate(); // negate(arg);
}
} else if (op == Ops.AND) {
// return asDocument("$and", collectConnectorArgs("$and", expr));
return collectConnectorArgs("$and", expr);
} else if (op == Ops.OR) {
// return asDocument("$or", collectConnectorArgs("$or", expr));
return collectConnectorArgs("$or", expr);
} else if (op == Ops.NE) {
// Path<?> path = (Path<?>) expr.getArg(0);
// Constant<?> constant = (Constant<?>) expr.getArg(1);
// return asDocument(asDBKey(expr, 0), asDocument("$ne", convert(path, constant)));
return QueryCriteria.where(asDBKey(expr, 0)).ne(asDBValue(expr, 1));
} else if (op == Ops.STARTS_WITH) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression("^" + regexValue(expr, 1)));
return QueryCriteria.where(asDBKey(expr, 0)).startingWith(asDBValue(expr, 1));
} else if (op == Ops.STARTS_WITH_IC) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression("^" + regexValue(expr, 1), "i"));
return QueryCriteria.where(asDBKey(expr, 0)).startingWith(true, asDBValue(expr, 1).toString());
} else if (op == Ops.ENDS_WITH) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(regexValue(expr, 1) + "$"));
return QueryCriteria.where(asDBKey(expr, 0)).endingWith(asDBValue(expr, 1));
} else if (op == Ops.ENDS_WITH_IC) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(regexValue(expr, 1) + "$", "i"));
return QueryCriteria.where(asDBKey(expr, 0)).endingWith(true, asDBValue(expr, 1).toString());
} else if (op == Ops.EQ_IGNORE_CASE) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression("^" + regexValue(expr, 1) + "$", "i"));
return QueryCriteria.where(asDBKey(expr, 0)).eq(true, asDBValue(expr, 1).toString());
} else if (op == Ops.STRING_CONTAINS) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(".*" + regexValue(expr, 1) + ".*"));
return QueryCriteria.where(asDBKey(expr, 0)).containing(asDBValue(expr, 1));
} else if (op == Ops.STRING_CONTAINS_IC) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(".*" + regexValue(expr, 1) + ".*", "i"));
return QueryCriteria.where(asDBKey(expr, 0)).containing(true, asDBValue(expr, 1).toString());
/*
} else if (op == Ops.MATCHES) {
//return asDocument(asDBKey(expr, 0), new CBRegularExpression(asDBValue(expr, 1).toString()));
return QueryCriteria.where(asDBKey(expr, 0)).like(asDBValue(expr,1));
} else if (op == Ops.MATCHES_IC) {
//return asDocument(asDBKey(expr, 0), new CBRegularExpression(asDBValue(expr, 1).toString(), "i"));
return QueryCriteria.where("UPPER("+asDBKey(expr, 0)+")").like("UPPER("+asDBValue(expr,1)+")");
*/
} else if (op == Ops.LIKE) {
// String regex = ExpressionUtils.likeToRegex((Expression) expr.getArg(1)).toString();
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(regex));
return QueryCriteria.where(asDBKey(expr, 0)).like(asDBValue(expr, 1));
} else if (op == Ops.LIKE_IC) {
// String regex = ExpressionUtils.likeToRegex((Expression) expr.getArg(1)).toString();
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(regex, "i"));
return QueryCriteria.where(asDBKey(expr, 0)).like(true, asDBValue(expr, 1).toString());
} else if (op == Ops.BETWEEN) {
// Document value = new Document("$gte", this.asDBValue(expr, 1));
// value.append("$lte", this.asDBValue(expr, 2));
// return this.asDocument(this.asDBKey(expr, 0), value);
return QueryCriteria.where(asDBKey(expr, 0)).between(asDBValue(expr, 1), asDBValue(expr, 2));
} else if (op == Ops.IN) {
int constIndex = 0;
int exprIndex = 1;
if (expr.getArg(1) instanceof Constant<?>) {
constIndex = 1;
exprIndex = 0;
}
if (Collection.class.isAssignableFrom(expr.getArg(constIndex).getType())) {
@SuppressWarnings("unchecked") // guarded by previous check
Collection<?> values = ((Constant<? extends Collection<?>>) expr.getArg(constIndex)).getConstant();
// return asDocument(asDBKey(expr, exprIndex), asDocument("$in", values));
return QueryCriteria.where(asDBKey(expr, exprIndex)).in(values);
} else { // I think framework already converts IN to EQ if arg is not a collection
// Path<?> path = (Path<?>) expr.getArg(exprIndex);
// Constant<?> constant = (Constant<?>) expr.getArg(constIndex);
// return asDocument(asDBKey(expr, exprIndex), convert(path, constant));
Object value = expr.getArg(constIndex);
return QueryCriteria.where(asDBKey(expr, exprIndex)).eq(value);
}
} else if (op == Ops.NOT_IN) {
int constIndex = 0;
int exprIndex = 1;
if (expr.getArg(1) instanceof Constant<?>) {
constIndex = 1;
exprIndex = 0;
}
if (Collection.class.isAssignableFrom(expr.getArg(constIndex).getType())) {
@SuppressWarnings("unchecked") // guarded by previous check
Collection<?> values = ((Constant<? extends Collection<?>>) expr.getArg(constIndex)).getConstant();
// return asDocument(asDBKey(expr, exprIndex), asDocument("$nin", values));
return QueryCriteria.where(asDBKey(expr, exprIndex)).notIn(values);
} else { // I think framework already converts NOT_IN to NE if arg is not a collection
// Path<?> path = (Path<?>) expr.getArg(exprIndex);
// Constant<?> constant = (Constant<?>) expr.getArg(constIndex);
// return asDocument(asDBKey(expr, exprIndex), asDocument("$ne", convert(path, constant)));
Object value = expr.getArg(constIndex);
return QueryCriteria.where(asDBKey(expr, exprIndex)).ne(value);
}
} else if (op == Ops.COL_IS_EMPTY) {
// List<Object> list = new ArrayList<Object>(2);
// list.add(asDocument(asDBKey(expr, 0), new ArrayList<Object>()));
// list.add(asDocument(asDBKey(expr, 0), asDocument("$exists", false)));
// return asDocument("$or", list);
return QueryCriteria.where(asDBKey(expr, 0)).isNotValued();
} else if (op == Ops.LT) {
// return asDocument(asDBKey(expr, 0), asDocument("$lt", asDBValue(expr, 1)));
return QueryCriteria.where(asDBKey(expr, 0)).lt(asDBValue(expr, 1));
} else if (op == Ops.GT) {
// return asDocument(asDBKey(expr, 0), asDocument("$gt", asDBValue(expr, 1)));
return QueryCriteria.where(asDBKey(expr, 0)).gt(asDBValue(expr, 1));
} else if (op == Ops.LOE) {
// return asDocument(asDBKey(expr, 0), asDocument("$lte", asDBValue(expr, 1)));
return QueryCriteria.where(asDBKey(expr, 0)).lte(asDBValue(expr, 1));
} else if (op == Ops.GOE) {
// return asDocument(asDBKey(expr, 0), asDocument("$gte", asDBValue(expr, 1)));
return QueryCriteria.where(asDBKey(expr, 0)).gte(asDBValue(expr, 1));
} else if (op == Ops.IS_NULL) {
// return asDocument(asDBKey(expr, 0), asDocument("$exists", false));
return QueryCriteria.where(asDBKey(expr, 0)).isNull();
} else if (op == Ops.IS_NOT_NULL) {
// return asDocument(asDBKey(expr, 0), asDocument("$exists", true));
return QueryCriteria.where(asDBKey(expr, 0)).isNotNull();
} else if (op == Ops.CONTAINS_KEY) { // TODO not sure about this one
Path<?> path = (Path<?>) expr.getArg(0);
// Expression<?> key = expr.getArg(1);
// return asDocument(visit(path, context) + "." + key.toString(), asDocument("$exists", true));
return QueryCriteria.where("meta().id"/*asDBKey(expr, 0)*/).eq(asDBKey(expr, 1));
} else if (op == Ops.STRING_LENGTH) {
return "LENGTH(" + asDBKey(expr, 0) + ")";// QueryCriteria.where(asDBKey(expr, 0)).size();
}
throw new UnsupportedOperationException("Illegal operation " + expr);
}
/* TODO -- need later
private Object negate(QueryCriteriaDefinition arg) {
List<Object> list = new ArrayList<Object>();
for (Map.Entry<String, Object> entry : arg.entrySet()) {
if (entry.getKey().equals("$or")) {
list.add(asDocument("$nor", entry.getValue()));
} else if (entry.getKey().equals("$and")) {
List<Object> list2 = new ArrayList<Object>();
for (Object o : ((Collection) entry.getValue())) {
list2.add(negate((QueryCriteriaDefinition) o));
}
list.add(asDocument("$or", list2));
} else if (entry.getValue() instanceof Pattern || entry.getValue() instanceof CBRegularExpression) {
list.add(asDocument(entry.getKey(), asDocument("$not", entry.getValue())));
} else if (entry.getValue() instanceof QueryCriteriaDefinition) {
list.add(negate(entry.getKey(), (QueryCriteriaDefinition) entry.getValue()));
} else {
list.add(asDocument(entry.getKey(), asDocument("$ne", entry.getValue())));
}
}
return list.size() == 1 ? list.get(0) : asDocument("$or", list);
}
private Object negate(String key, QueryCriteriaDefinition value) {
if (value.size() == 1) {
return asDocument(key, asDocument("$not", value));
} else {
List<Object> list2 = new ArrayList<Object>();
for (Map.Entry<String, Object> entry2 : value.entrySet()) {
list2.add(asDocument(key, asDocument("$not", asDocument(entry2.getKey(), entry2.getValue()))));
}
return asDocument("$or", list2);
}
}
*/
/* TODO -- need later
protected Object convert(Path<?> property, Constant<?> constant) {
if (isReference(property)) {
return asReference(constant.getConstant());
} else if (isId(property)) {
if (isReference(property.getMetadata().getParent())) {
return asReferenceKey(property.getMetadata().getParent().getType(), constant.getConstant());
} else if (constant.getType().equals(String.class) && isImplicitObjectIdConversion()) {
String id = (String) constant.getConstant();
return ObjectId.isValid(id) ? new ObjectId(id) : id;
}
}
return visit(constant, null);
}
*/
protected boolean isImplicitObjectIdConversion() {
return true;
}
protected DBRef asReferenceKey(Class<?> entity, Object id) {
// TODO override in subclass
throw new UnsupportedOperationException();
}
protected abstract DBRef asReference(Object constant);
protected abstract boolean isReference(Path<?> arg);
protected boolean isId(Path<?> arg) {
// TODO override in subclass
return false;
}
@Override
public String visit(Path<?> expr, Void context) {
PathMetadata metadata = expr.getMetadata();
if (metadata.getParent() != null) {
Path<?> parent = metadata.getParent();
if (parent.getMetadata().getPathType() == PathType.DELEGATE) {
parent = parent.getMetadata().getParent();
}
if (metadata.getPathType() == PathType.COLLECTION_ANY) {
return visit(parent, context);
} else if (parent.getMetadata().getPathType() != PathType.VARIABLE) {
String rv = getKeyForPath(expr, metadata);
String parentStr = visit(parent, context);
return rv != null ? parentStr + "." + rv : parentStr;
}
}
return getKeyForPath(expr, metadata);
}
protected String getKeyForPath(Path<?> expr, PathMetadata metadata) {
return metadata.getElement().toString();
}
@Override
public Object visit(SubQueryExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(ParamExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
private QueryCriteriaDefinition collectConnectorArgs(String operator, Operation<?> operation) {
QueryCriteria first = null;
for (Expression<?> exp : operation.getArgs()) {
QueryCriteria document = (QueryCriteria) handle(exp);
if (first == null) {
first = document;
} else {
if (operator.equals("$or")) {
first = first.or(document);
} else if (operator.equals("$and")) {
first = first.and(document);
}
}
}
return first;
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase;
import java.io.Closeable;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.Scope;
/**
* The {@link CouchbaseClientFactory} is the main way to get access to the managed SDK instance and resources.
* <p>
* Please note that a single factory is always bound to a {@link Bucket}, so if you need to access more than one
* you need to initialize one factory for each.
*/
public interface CouchbaseClientFactory extends Closeable {
/**
* Provides access to the managed SDK {@link Cluster} reference.
*/
Cluster getCluster();
/**
* Provides access to the managed SDK {@link Bucket} reference.
*/
Bucket getBucket();
/**
* Provides access to the managed SDK {@link Scope} reference.
*/
Scope getScope();
/**
* Provides access to a collection (identified by its name) in managed SDK {@link Scope} reference.
*
* @param name the name of the collection. If null is passed in, the default collection is assumed.
*/
Collection getCollection(String name);
/**
* Provides access to the default collection.
*/
Collection getDefaultCollection();
/**
* Returns a new {@link CouchbaseClientFactory} set to the scope given as an argument.
*
* @param scopeName the name of the scope to use for all collection access.
* @return a new client factory, bound to the other scope.
*/
CouchbaseClientFactory withScope(String scopeName);
/**
* The exception translator used on the factory.
*/
PersistenceExceptionTranslator getExceptionTranslator();
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase;
import java.util.function.Supplier;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.couchbase.core.CouchbaseExceptionTranslator;
import com.couchbase.client.core.env.Authenticator;
import com.couchbase.client.core.env.OwnedSupplier;
import com.couchbase.client.core.io.CollectionIdentifier;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.ClusterOptions;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.Scope;
import com.couchbase.client.java.env.ClusterEnvironment;
/**
* The default implementation of a {@link CouchbaseClientFactory}.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
public class SimpleCouchbaseClientFactory implements CouchbaseClientFactory {
private final Supplier<Cluster> cluster;
private final Bucket bucket;
private final Scope scope;
private final PersistenceExceptionTranslator exceptionTranslator;
public SimpleCouchbaseClientFactory(final String connectionString, final Authenticator authenticator,
final String bucketName) {
this(connectionString, authenticator, bucketName, null);
}
public SimpleCouchbaseClientFactory(final String connectionString, final Authenticator authenticator,
final String bucketName, final String scopeName) {
this(new OwnedSupplier<>(Cluster.connect(connectionString, ClusterOptions.clusterOptions(authenticator))),
bucketName, scopeName);
}
public SimpleCouchbaseClientFactory(final String connectionString, final Authenticator authenticator,
final String bucketName, final String scopeName, final ClusterEnvironment environment) {
this(
new OwnedSupplier<>(
Cluster.connect(connectionString, ClusterOptions.clusterOptions(authenticator).environment(environment))),
bucketName, scopeName);
}
public SimpleCouchbaseClientFactory(final Cluster cluster, final String bucketName, final String scopeName) {
this(() -> cluster, bucketName, scopeName);
}
private SimpleCouchbaseClientFactory(final Supplier<Cluster> cluster, final String bucketName,
final String scopeName) {
this.cluster = cluster;
this.bucket = cluster.get().bucket(bucketName);
this.scope = scopeName == null ? bucket.defaultScope() : bucket.scope(scopeName);
this.exceptionTranslator = new CouchbaseExceptionTranslator();
}
@Override
public CouchbaseClientFactory withScope(final String scopeName) {
return new SimpleCouchbaseClientFactory(cluster, bucket.name(), scopeName != null ? scopeName : getScope().name());
}
@Override
public Cluster getCluster() {
return cluster.get();
}
@Override
public Bucket getBucket() {
return bucket;
}
@Override
public Scope getScope() {
return scope;
}
@Override
public Collection getCollection(final String collectionName) {
final Scope scope = getScope();
if (collectionName == null || CollectionIdentifier.DEFAULT_COLLECTION.equals(collectionName)) {
if(scope != null ) {
if (scope.name() != null && !CollectionIdentifier.DEFAULT_SCOPE.equals(scope.name())) {
throw new IllegalStateException("A collectionName must be provided if a non-default scope is used");
}
}
return getBucket().defaultCollection();
}
return scope.collection(collectionName);
}
@Override
public Collection getDefaultCollection() {
return getCollection(null);
}
@Override
public PersistenceExceptionTranslator getExceptionTranslator() {
return exceptionTranslator;
}
@Override
public void close() {
if (cluster instanceof OwnedSupplier) {
cluster.get().disconnect();
}
}
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.cache;
import org.springframework.util.Assert;
/**
* {@link CacheKeyPrefix} provides a hook for creating custom prefixes prepended to the actual {@literal key} stored in
* Couchbase.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Michael Nitschinger
* @since 4.0.0
*/
@FunctionalInterface
public interface CacheKeyPrefix {
/**
* Default separator.
*
* @since 2.3
*/
String SEPARATOR = "::";
/**
* Creates a default {@link CacheKeyPrefix} scheme that prefixes cache keys with {@code cacheName} followed by double
* colons. A cache named {@code myCache} will prefix all cache keys with {@code myCache::}.
*
* @return the default {@link CacheKeyPrefix} scheme.
*/
static CacheKeyPrefix simple() {
return name -> name + SEPARATOR;
}
/**
* Creates a {@link CacheKeyPrefix} scheme that prefixes cache keys with the given {@code prefix}. The prefix is
* prepended to the {@code cacheName} followed by double colons. A prefix {@code cb-} with a cache named
* {@code myCache} results in {@code cb-myCache::}.
*
* @param prefix must not be {@literal null}.
* @return the default {@link CacheKeyPrefix} scheme.
* @since 4.0.0
*/
static CacheKeyPrefix prefixed(final String prefix) {
Assert.notNull(prefix, "Prefix must not be null!");
return name -> prefix + name + SEPARATOR;
}
/**
* Compute the prefix for the actual {@literal key} stored in Couchbase.
*
* @param cacheName will never be {@literal null}.
* @return never {@literal null}.
*/
String compute(String cacheName);
}

View File

@@ -1,234 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.cache;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.Callable;
import org.springframework.cache.support.AbstractValueAdaptingCache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
public class CouchbaseCache extends AbstractValueAdaptingCache {
private final String name;
private final CouchbaseCacheWriter cacheWriter;
private final CouchbaseCacheConfiguration cacheConfig;
private final ConversionService conversionService;
protected CouchbaseCache(final String name, final CouchbaseCacheWriter cacheWriter,
final CouchbaseCacheConfiguration cacheConfig) {
super(cacheConfig.getAllowCacheNullValues());
Assert.notNull(name, "Name must not be null!");
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
Assert.notNull(cacheConfig, "CacheConfig must not be null!");
this.name = name;
this.cacheWriter = cacheWriter;
this.cacheConfig = cacheConfig;
this.conversionService = cacheConfig.getConversionService();
}
private static <T> T valueFromLoader(Object key, Callable<T> valueLoader) {
try {
return valueLoader.call();
} catch (Exception e) {
throw new ValueRetrievalException(key, valueLoader, e);
}
}
@Override
public String getName() {
return name;
}
@Override
public CouchbaseCacheWriter getNativeCache() {
return cacheWriter;
}
@Override
protected Object lookup(final Object key) {
return cacheWriter.get(cacheConfig.getCollectionName(), createCacheKey(key), cacheConfig.getValueTranscoder());
}
/**
* Returns the configuration for this {@link CouchbaseCache}.
*/
public CouchbaseCacheConfiguration getCacheConfiguration() {
return cacheConfig;
}
@Override
@SuppressWarnings("unchecked")
public synchronized <T> T get(final Object key, final Callable<T> valueLoader) {
ValueWrapper result = get(key);
if (result != null) {
return (T) result.get();
}
T value = valueFromLoader(key, valueLoader);
put(key, value);
return value;
}
@Override
public void put(final Object key, final Object value) {
if (!isAllowNullValues() && value == null) {
throw new IllegalArgumentException(String.format(
"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or "
+ "configure CouchbaseCache to allow 'null' via CouchbaseCacheConfiguration.",
name));
}
cacheWriter.put(cacheConfig.getCollectionName(), createCacheKey(key), toStoreValue(value), cacheConfig.getExpiry(),
cacheConfig.getValueTranscoder());
}
@Override
public ValueWrapper putIfAbsent(final Object key, final Object value) {
if (!isAllowNullValues() && value == null) {
return get(key);
}
Object result = cacheWriter.putIfAbsent(cacheConfig.getCollectionName(), createCacheKey(key), toStoreValue(value),
cacheConfig.getExpiry(), cacheConfig.getValueTranscoder());
if (result == null) {
return null;
}
return new SimpleValueWrapper(result);
}
@Override
public void evict(final Object key) {
cacheWriter.remove(cacheConfig.getCollectionName(), createCacheKey(key));
}
@Override
public boolean evictIfPresent(final Object key) {
return cacheWriter.remove(cacheConfig.getCollectionName(), createCacheKey(key));
}
@Override
public boolean invalidate() {
return cacheWriter.clear(cacheConfig.getCollectionName(), cacheConfig.getKeyPrefixFor(name)) > 0;
}
@Override
public void clear() {
cacheWriter.clear( cacheConfig.getCollectionName(), cacheConfig.getKeyPrefixFor(name));
}
/**
* Customization hook for creating cache key before it gets serialized.
*
* @param key will never be {@literal null}.
* @return never {@literal null}.
*/
protected String createCacheKey(final Object key) {
String convertedKey = convertKey(key);
if (!cacheConfig.usePrefix()) {
return convertedKey;
}
return prefixCacheKey(convertedKey);
}
/**
* Convert {@code key} to a {@link String} representation used for cache key creation.
*
* @param key will never be {@literal null}.
* @return never {@literal null}.
* @throws IllegalStateException if {@code key} cannot be converted to {@link String}.
*/
protected String convertKey(final Object key) {
if (key instanceof String) {
return (String) key;
}
TypeDescriptor source = TypeDescriptor.forObject(key);
if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
try {
return conversionService.convert(key, String.class);
} catch (ConversionFailedException e) {
// may fail if the given key is a collection
if (isCollectionLikeOrMap(source)) {
return convertCollectionLikeOrMapKey(key, source);
}
throw e;
}
}
Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");
if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
return key.toString();
}
throw new IllegalStateException(String.format(
"Cannot convert cache key %s to String. Please register a suitable Converter via "
+ "'CouchbaseCacheConfiguration.configureKeyConverters(...)' or override '%s.toString()'.",
source, key.getClass().getSimpleName()));
}
private String prefixCacheKey(final String key) {
// allow contextual cache names by computing the key prefix on every call.
return cacheConfig.getKeyPrefixFor(name) + key;
}
private boolean isCollectionLikeOrMap(final TypeDescriptor source) {
return source.isArray() || source.isCollection() || source.isMap();
}
private String convertCollectionLikeOrMapKey(final Object key, final TypeDescriptor source) {
if (source.isMap()) {
StringBuilder target = new StringBuilder("{");
for (Map.Entry<?, ?> entry : ((Map<?, ?>) key).entrySet()) {
target.append(convertKey(entry.getKey())).append("=").append(convertKey(entry.getValue()));
}
target.append("}");
return target.toString();
} else if (source.isCollection() || source.isArray()) {
StringJoiner sj = new StringJoiner(",");
Collection<?> collection = source.isCollection() ? (Collection<?>) key
: Arrays.asList(ObjectUtils.toObjectArray(key));
for (Object val : collection) {
sj.add(convertKey(val));
}
return "[" + sj.toString() + "]";
}
throw new IllegalArgumentException(String.format("Cannot convert cache key %s to String.", key));
}
}

View File

@@ -1,206 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.cache;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.util.Assert;
import com.couchbase.client.java.codec.SerializableTranscoder;
import com.couchbase.client.java.codec.Transcoder;
public class CouchbaseCacheConfiguration {
private final Duration expiry;
private final boolean cacheNullValues;
private final CacheKeyPrefix keyPrefix;
private final boolean usePrefix;
private final Transcoder valueTranscoder;
private final ConversionService conversionService;
private final String collectionName;
private CouchbaseCacheConfiguration(final Duration expiry, final boolean cacheNullValues, final boolean usePrefix,
final CacheKeyPrefix keyPrefix, final ConversionService conversionService, final Transcoder valueTranscoder,
final String collectionName) {
this.expiry = expiry;
this.cacheNullValues = cacheNullValues;
this.usePrefix = usePrefix;
this.keyPrefix = keyPrefix;
this.conversionService = conversionService;
this.valueTranscoder = valueTranscoder;
this.collectionName = collectionName;
}
public static CouchbaseCacheConfiguration defaultCacheConfig() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
registerDefaultConverters(conversionService);
return new CouchbaseCacheConfiguration(Duration.ZERO, true, true, CacheKeyPrefix.simple(), conversionService,
SerializableTranscoder.INSTANCE, null);
}
/**
* Registers default cache key converters. The following converters get registered:
* <ul>
* <li>{@link String} to {@link byte byte[]} using UTF-8 encoding.</li>
* <li>{@link SimpleKey} to {@link String}</li>
* </ul>
*
* @param registry must not be {@literal null}.
*/
public static void registerDefaultConverters(final ConverterRegistry registry) {
Assert.notNull(registry, "ConverterRegistry must not be null!");
registry.addConverter(String.class, byte[].class, source -> source.getBytes(StandardCharsets.UTF_8));
registry.addConverter(SimpleKey.class, String.class, SimpleKey::toString);
}
/**
* Set the expiry to apply for cache entries. Use {@link Duration#ZERO} to declare an eternal cache.
*
* @param expiry must not be {@literal null}.
* @return new {@link CouchbaseCacheConfiguration}.
*/
public CouchbaseCacheConfiguration entryExpiry(final Duration expiry) {
Assert.notNull(expiry, "Expiry duration must not be null!");
return new CouchbaseCacheConfiguration(expiry, cacheNullValues, usePrefix, keyPrefix, conversionService,
valueTranscoder, collectionName);
}
/**
* Set the collectinName to use.
*
* @param collectionName must not be {@literal null}.
* @return new {@link CouchbaseCacheConfiguration}.
*/
public CouchbaseCacheConfiguration collection(final String collectionName) {
Assert.notNull(collectionName, "collectionName must not be null!");
return new CouchbaseCacheConfiguration(expiry, cacheNullValues, usePrefix, keyPrefix, conversionService,
valueTranscoder, collectionName);
}
/**
* Sets a custom transcoder to use for reads and writes.
*
* @param valueTranscoder the transcoder that should be used.
* @return new {@link CouchbaseCacheConfiguration}.
*/
public CouchbaseCacheConfiguration valueTranscoder(final Transcoder valueTranscoder) {
Assert.notNull(valueTranscoder, "Transcoder must not be null!");
return new CouchbaseCacheConfiguration(expiry, cacheNullValues, usePrefix, keyPrefix, conversionService,
valueTranscoder, collectionName);
}
/**
* Disable caching {@literal null} values. <br />
* <strong>NOTE</strong> any {@link org.springframework.cache.Cache#put(Object, Object)} operation involving
* {@literal null} value will error. Nothing will be written to Couchbase, nothing will be removed. An already
* existing key will still be there afterwards with the very same value as before.
*
* @return new {@link CouchbaseCacheConfiguration}.
*/
public CouchbaseCacheConfiguration disableCachingNullValues() {
return new CouchbaseCacheConfiguration(expiry, false, usePrefix, keyPrefix, conversionService, valueTranscoder,
collectionName);
}
/**
* Prefix the {@link CouchbaseCache#getName() cache name} with the given value. <br />
* The generated cache key will be: {@code prefix + cache name + "::" + cache entry key}.
*
* @param prefix the prefix to prepend to the cache name.
* @return this.
* @see #computePrefixWith(CacheKeyPrefix)
* @see CacheKeyPrefix#prefixed(String)
*/
public CouchbaseCacheConfiguration prefixCacheNameWith(final String prefix) {
return computePrefixWith(CacheKeyPrefix.prefixed(prefix));
}
/**
* Use the given {@link CacheKeyPrefix} to compute the prefix for the actual Couchbase {@literal key} given the
* {@literal cache name} as function input.
*
* @param cacheKeyPrefix must not be {@literal null}.
* @return new {@link CouchbaseCacheConfiguration}.
* @see CacheKeyPrefix
*/
public CouchbaseCacheConfiguration computePrefixWith(CacheKeyPrefix cacheKeyPrefix) {
Assert.notNull(cacheKeyPrefix, "Function for computing prefix must not be null!");
return new CouchbaseCacheConfiguration(expiry, cacheNullValues, true, cacheKeyPrefix, conversionService,
valueTranscoder, collectionName);
}
/**
* @return The expiration time (ttl) for cache entries. Never {@literal null}.
*/
public Duration getExpiry() {
return expiry;
}
/**
* @return {@literal true} if caching {@literal null} is allowed.
*/
public boolean getAllowCacheNullValues() {
return cacheNullValues;
}
/**
* @return The {@link ConversionService} used for cache key to {@link String} conversion. Never {@literal null}.
*/
public ConversionService getConversionService() {
return conversionService;
}
/**
* @return {@literal true} if cache keys need to be prefixed with the {@link #getKeyPrefixFor(String)} if present or
* the default which resolves to {@link Cache#getName()}.
*/
public boolean usePrefix() {
return usePrefix;
}
/**
* Get the computed {@literal key} prefix for a given {@literal cacheName}.
*
* @return never {@literal null}.
*/
public String getKeyPrefixFor(final String cacheName) {
Assert.notNull(cacheName, "Cache name must not be null!");
return keyPrefix.compute(cacheName);
}
/**
* Get the transcoder for encoding and decoding cache values.
*/
public Transcoder getValueTranscoder() {
return valueTranscoder;
}
/**
* The name of the collection to use for this cache - if empty uses the default collection.
*/
public String getCollectionName() {
return collectionName;
}
}

View File

@@ -1,278 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.cache;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
public class CouchbaseCacheManager extends AbstractTransactionSupportingCacheManager {
private final CouchbaseCacheWriter cacheWriter;
private final CouchbaseCacheConfiguration defaultCacheConfig;
private final Map<String, CouchbaseCacheConfiguration> initialCacheConfiguration;
private final boolean allowInFlightCacheCreation;
/**
* Creates new {@link CouchbaseCacheManager} using given {@link CouchbaseCacheWriter} and default
* {@link CouchbaseCacheConfiguration}.
*
* @param cacheWriter must not be {@literal null}.
* @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
* {@link CouchbaseCacheConfiguration#defaultCacheConfig()}.
* @param allowInFlightCacheCreation allow create unconfigured caches.
*/
private CouchbaseCacheManager(final CouchbaseCacheWriter cacheWriter,
final CouchbaseCacheConfiguration defaultCacheConfiguration,
final Map<String, CouchbaseCacheConfiguration> initialCacheConfiguration,
final boolean allowInFlightCacheCreation) {
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
this.initialCacheConfiguration = initialCacheConfiguration;
this.allowInFlightCacheCreation = allowInFlightCacheCreation;
}
/**
* Create a new {@link CouchbaseCacheManager} with defaults applied.
*
* @param clientFactory must not be {@literal null}.
* @return new instance of {@link CouchbaseCacheManager}.
*/
public static CouchbaseCacheManager create(CouchbaseClientFactory clientFactory) {
Assert.notNull(clientFactory, "ConnectionFactory must not be null!");
return new CouchbaseCacheManager(new DefaultCouchbaseCacheWriter(clientFactory),
CouchbaseCacheConfiguration.defaultCacheConfig(), new LinkedHashMap<>(), true);
}
/**
* Entry point for builder style {@link CouchbaseCacheManager} configuration.
*
* @param clientFactory must not be {@literal null}.
* @return new {@link CouchbaseCacheManagerBuilder}.
*/
public static CouchbaseCacheManagerBuilder builder(CouchbaseClientFactory clientFactory) {
Assert.notNull(clientFactory, "ConnectionFactory must not be null!");
return CouchbaseCacheManagerBuilder.fromConnectionFactory(clientFactory);
}
/**
* Entry point for builder style {@link CouchbaseCacheManager} configuration.
*
* @param cacheWriter must not be {@literal null}.
* @return new {@link CouchbaseCacheManagerBuilder}.
*/
public static CouchbaseCacheManagerBuilder builder(CouchbaseCacheWriter cacheWriter) {
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
return CouchbaseCacheManagerBuilder.fromCacheWriter(cacheWriter);
}
@Override
protected Collection<? extends Cache> loadCaches() {
final List<CouchbaseCache> caches = new LinkedList<>();
for (Map.Entry<String, CouchbaseCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
caches.add(createCouchbaseCache(entry.getKey(), entry.getValue()));
}
return caches;
}
@Override
protected CouchbaseCache getMissingCache(final String name) {
return allowInFlightCacheCreation ? createCouchbaseCache(name, defaultCacheConfig) : null;
}
/**
* Configuration hook for creating {@link CouchbaseCache} with given name and {@code cacheConfig}.
*
* @param name must not be {@literal null}.
* @param cacheConfig can be {@literal null}.
* @return never {@literal null}.
*/
protected CouchbaseCache createCouchbaseCache(final String name,
@Nullable final CouchbaseCacheConfiguration cacheConfig) {
return new CouchbaseCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
public static class CouchbaseCacheManagerBuilder {
private final CouchbaseCacheWriter cacheWriter;
private final Map<String, CouchbaseCacheConfiguration> initialCaches = new LinkedHashMap<>();
boolean allowInFlightCacheCreation = true;
private CouchbaseCacheConfiguration defaultCacheConfiguration = CouchbaseCacheConfiguration.defaultCacheConfig();
private boolean enableTransactions;
private CouchbaseCacheManagerBuilder(CouchbaseCacheWriter cacheWriter) {
this.cacheWriter = cacheWriter;
}
/**
* Entry point for builder style {@link CouchbaseCacheManager} configuration.
*
* @param clientFactory must not be {@literal null}.
* @return new {@link CouchbaseCacheManagerBuilder}.
*/
public static CouchbaseCacheManagerBuilder fromConnectionFactory(CouchbaseClientFactory clientFactory) {
Assert.notNull(clientFactory, "ConnectionFactory must not be null!");
return builder(new DefaultCouchbaseCacheWriter(clientFactory));
}
/**
* Entry point for builder style {@link CouchbaseCacheManager} configuration.
*
* @param cacheWriter must not be {@literal null}.
* @return new {@link CouchbaseCacheManagerBuilder}.
*/
public static CouchbaseCacheManagerBuilder fromCacheWriter(CouchbaseCacheWriter cacheWriter) {
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
return new CouchbaseCacheManagerBuilder(cacheWriter);
}
/**
* Define a default {@link CouchbaseCacheConfiguration} applied to dynamically created {@link CouchbaseCache}s.
*
* @param defaultCacheConfiguration must not be {@literal null}.
* @return this {@link CouchbaseCacheManagerBuilder}.
*/
public CouchbaseCacheManagerBuilder cacheDefaults(CouchbaseCacheConfiguration defaultCacheConfiguration) {
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
this.defaultCacheConfiguration = defaultCacheConfiguration;
return this;
}
/**
* Enable {@link CouchbaseCache}s to synchronize cache put/evict operations with ongoing Spring-managed
* transactions.
*
* @return this {@link CouchbaseCacheManagerBuilder}.
*/
public CouchbaseCacheManagerBuilder transactionAware() {
this.enableTransactions = true;
return this;
}
/**
* Append a {@link Set} of cache names to be pre initialized with current {@link CouchbaseCacheConfiguration}.
* <strong>NOTE:</strong> This calls depends on {@link #cacheDefaults(CouchbaseCacheConfiguration)} using whatever
* default {@link CouchbaseCacheConfiguration} is present at the time of invoking this method.
*
* @param cacheNames must not be {@literal null}.
* @return this {@link CouchbaseCacheManagerBuilder}.
*/
public CouchbaseCacheManagerBuilder initialCacheNames(Set<String> cacheNames) {
Assert.notNull(cacheNames, "CacheNames must not be null!");
cacheNames.forEach(it -> withCacheConfiguration(it, defaultCacheConfiguration));
return this;
}
/**
* Append a {@link Map} of cache name/{@link CouchbaseCacheConfiguration} pairs to be pre initialized.
*
* @param cacheConfigurations must not be {@literal null}.
* @return this {@link CouchbaseCacheManagerBuilder}.
*/
public CouchbaseCacheManagerBuilder withInitialCacheConfigurations(
Map<String, CouchbaseCacheConfiguration> cacheConfigurations) {
Assert.notNull(cacheConfigurations, "CacheConfigurations must not be null!");
cacheConfigurations.forEach((cacheName, configuration) -> Assert.notNull(configuration,
String.format("CouchbaseCacheConfiguration for cache %s must not be null!", cacheName)));
this.initialCaches.putAll(cacheConfigurations);
return this;
}
/**
* @param cacheName
* @param cacheConfiguration
* @return this {@link CouchbaseCacheManagerBuilder}.
*/
public CouchbaseCacheManagerBuilder withCacheConfiguration(String cacheName,
CouchbaseCacheConfiguration cacheConfiguration) {
Assert.notNull(cacheName, "CacheName must not be null!");
Assert.notNull(cacheConfiguration, "CacheConfiguration must not be null!");
this.initialCaches.put(cacheName, cacheConfiguration);
return this;
}
/**
* Disable in-flight {@link org.springframework.cache.Cache} creation for unconfigured caches.
* <p>
* {@link CouchbaseCacheManager#getMissingCache(String)} returns {@literal null} for any unconfigured
* {@link org.springframework.cache.Cache} instead of a new {@link CouchbaseCache} instance. This allows eg.
* {@link org.springframework.cache.support.CompositeCacheManager} to chime in.
*
* @return this {@link CouchbaseCacheManagerBuilder}.
*/
public CouchbaseCacheManagerBuilder disableCreateOnMissingCache() {
this.allowInFlightCacheCreation = false;
return this;
}
/**
* Get the {@link Set} of cache names for which the builder holds {@link CouchbaseCacheConfiguration configuration}.
*
* @return an unmodifiable {@link Set} holding the name of caches for which a {@link CouchbaseCacheConfiguration
* configuration} has been set.
*/
public Set<String> getConfiguredCaches() {
return Collections.unmodifiableSet(this.initialCaches.keySet());
}
/**
* Get the {@link CouchbaseCacheConfiguration} for a given cache by its name.
*
* @param cacheName must not be {@literal null}.
* @return {@link Optional#empty()} if no {@link CouchbaseCacheConfiguration} set for the given cache name.
*/
public Optional<CouchbaseCacheConfiguration> getCacheConfigurationFor(String cacheName) {
return Optional.ofNullable(this.initialCaches.get(cacheName));
}
/**
* Create new instance of {@link CouchbaseCacheManager} with configuration options applied.
*
* @return new instance of {@link CouchbaseCacheManager}.
*/
public CouchbaseCacheManager build() {
CouchbaseCacheManager cm = new CouchbaseCacheManager(cacheWriter, defaultCacheConfiguration, initialCaches,
allowInFlightCacheCreation);
cm.setTransactionAware(enableTransactions);
return cm;
}
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.cache;
import java.time.Duration;
import org.springframework.lang.Nullable;
import com.couchbase.client.java.codec.Transcoder;
public interface CouchbaseCacheWriter {
/**
* Write the given key/value pair to Couchbase an set the expiration time if defined.
*
* @param collectionName The cache name must not be {@literal null}.
* @param key The key for the cache entry. Must not be {@literal null}.
* @param value The value stored for the key. Must not be {@literal null}.
* @param expiry Optional expiration time. Can be {@literal null}.
* @param transcoder Optional transcoder to use. Can be {@literal null}.
*/
void put(String collectionName, String key, Object value, @Nullable Duration expiry, @Nullable Transcoder transcoder);
/**
* Write the given value to Couchbase if the key does not already exist.
*
* @param collectionName The cache name must not be {@literal null}.
* @param key The key for the cache entry. Must not be {@literal null}.
* @param value The value stored for the key. Must not be {@literal null}.
* @param expiry Optional expiration time. Can be {@literal null}.
* @param transcoder Optional transcoder to use. Can be {@literal null}.
*/
@Nullable
Object putIfAbsent(String collectionName, String key, Object value, @Nullable Duration expiry,
@Nullable Transcoder transcoder);
/**
* Get the binary value representation from Couchbase stored for the given key.
*
* @param collectionName must not be {@literal null}.
* @param key must not be {@literal null}.
* @param transcoder Optional transcoder to use. Can be {@literal null}.
* @return {@literal null} if key does not exist.
*/
@Nullable
Object get(String collectionName, String key, @Nullable Transcoder transcoder);
/**
* Remove the given key from Couchbase.
*
* @param collectionName The cache name must not be {@literal null}.
* @param key The key for the cache entry. Must not be {@literal null}.
* @return true if the document existed on removal, false otherwise.
*/
boolean remove(String collectionName, String key);
/**
* Clears the cache with the given key pattern prefix.
*
* @param pattern the pattern to clear.
* @return the number of cleared items.
*/
long clear(String collectionName, String pattern);
}

View File

@@ -1,139 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.cache;
import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_COLLECTION;
import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_SCOPE;
import static com.couchbase.client.java.kv.GetOptions.*;
import static com.couchbase.client.java.kv.InsertOptions.*;
import static com.couchbase.client.java.kv.UpsertOptions.*;
import static com.couchbase.client.java.query.QueryOptions.*;
import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS;
import java.time.Duration;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import com.couchbase.client.core.error.DocumentExistsException;
import com.couchbase.client.core.error.DocumentNotFoundException;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.Scope;
import com.couchbase.client.java.codec.Transcoder;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.kv.InsertOptions;
import com.couchbase.client.java.kv.UpsertOptions;
import com.couchbase.client.java.query.QueryMetrics;
import com.couchbase.client.java.query.QueryResult;
public class DefaultCouchbaseCacheWriter implements CouchbaseCacheWriter {
private final CouchbaseClientFactory clientFactory;
public DefaultCouchbaseCacheWriter(final CouchbaseClientFactory clientFactory) {
this.clientFactory = clientFactory;
}
@Override
public void put(final String collectionName, final String key, final Object value, final Duration expiry,
final Transcoder transcoder) {
UpsertOptions options = upsertOptions();
if (expiry != null) {
options.expiry(expiry);
}
if (transcoder != null) {
options.transcoder(transcoder);
}
getCollection(collectionName).upsert(key, value, options);
}
@Override
public Object putIfAbsent(final String collectionName, final String key, final Object value, final Duration expiry,
final Transcoder transcoder) {
InsertOptions options = insertOptions();
if (expiry != null) {
options.expiry(expiry);
}
if (transcoder != null) {
options.transcoder(transcoder);
}
try {
getCollection(collectionName).insert(key, value, options);
return null;
} catch (final DocumentExistsException ex) {
// If the document exists, return the current one per contract
return get(collectionName, key, transcoder);
}
}
@Override
public Object get(final String collectionName, final String key, final Transcoder transcoder) {
// TODO .. the decoding side transcoding needs to be figured out?
try {
return getCollection(collectionName).get(key, getOptions().transcoder(transcoder)).contentAs(Object.class);
} catch (DocumentNotFoundException ex) {
return null;
}
}
@Override
public boolean remove(final String collectionName, final String key) {
try {
getCollection(collectionName).remove(key);
return true;
} catch (final DocumentNotFoundException ex) {
return false;
}
}
@Override
public long clear(final String collectionName, final String pattern) {
QueryResult result;
if (getScope() == null
|| (DEFAULT_SCOPE.equals(getScope().name()) && DEFAULT_COLLECTION.equals(getCollection(collectionName).name()))) {
result = clientFactory.getCluster().query(
"DELETE FROM `" + clientFactory.getBucket().name() + "` where meta().id LIKE $pattern",
queryOptions().scanConsistency(REQUEST_PLUS).metrics(true)
.parameters(JsonObject.create().put("pattern", pattern + "%")));
} else {
result = clientFactory.getScope().query(
"DELETE FROM `" + getCollection(collectionName).name() + "` where meta().id LIKE $pattern",
queryOptions().scanConsistency(REQUEST_PLUS).metrics(true)
.parameters(JsonObject.create().put("pattern", pattern + "%")));
}
return result.metaData().metrics().map(QueryMetrics::mutationCount).orElse(0L);
}
private Collection getCollection(final String collectionName) {
final Scope scope = clientFactory.getScope();
if (collectionName == null) {
if (!scope.name().equals(DEFAULT_SCOPE)) {
throw new IllegalStateException("A collectionName must be provided if a non-default scope is used!");
}
return clientFactory.getBucket().defaultCollection();
}
return scope.collection(collectionName);
}
private Scope getScope() {
return clientFactory.getScope();
}
}

View File

@@ -1,434 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.config;
import static com.couchbase.client.java.ClusterOptions.clusterOptions;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.transaction.CouchbaseCallbackTransactionManager;
import org.springframework.data.couchbase.transaction.CouchbaseTransactionInterceptor;
import org.springframework.data.couchbase.transaction.CouchbaseTransactionalOperator;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.couchbase.client.core.deps.com.fasterxml.jackson.databind.DeserializationFeature;
import com.couchbase.client.core.encryption.CryptoManager;
import com.couchbase.client.core.env.Authenticator;
import com.couchbase.client.core.env.PasswordAuthenticator;
import com.couchbase.client.core.error.CouchbaseException;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.codec.JacksonJsonSerializer;
import com.couchbase.client.java.encryption.databind.jackson.EncryptionModule;
import com.couchbase.client.java.env.ClusterEnvironment;
import com.couchbase.client.java.json.JacksonTransformers;
import com.couchbase.client.java.json.JsonValueModule;
import com.couchbase.client.java.query.QueryScanConsistency;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Base class for Spring Data Couchbase configuration using JavaConfig.
*
* @author Michael Nitschinger
* @author Simon Baslé
* @author Stephane Nicoll
* @author Subhashni Balakrishnan
* @author Jorge Rodriguez Martin
* @author Michael Reiche
*/
@Configuration
public abstract class AbstractCouchbaseConfiguration {
/**
* The connection string which allows the SDK to connect to the cluster.
* <p>
* Note that the connection string can take many forms, in its simplest it is just a single hostname like "127.0.0.1".
* Please refer to the couchbase Java SDK documentation for all the different possibilities and options.
*/
public abstract String getConnectionString();
/**
* The username of the user accessing Couchbase, configured on the cluster.
*/
public abstract String getUserName();
/**
* The password used or the username to authenticate against the cluster.
*/
public abstract String getPassword();
/**
* The name of the bucket that should be used (for example "travel-sample").
*/
public abstract String getBucketName();
/**
* If a non-default scope should be used, override this method.
*
* @return the custom scope name or null if the default scope should be used (default).
*/
protected String getScopeName() {
return null;
}
/**
* Allows to override the {@link Authenticator} used.
* <p>
* The default implementation uses the {@link PasswordAuthenticator} and takes the username and password from
* {@link #getUserName()} and {@link #getPassword()} respectively.
*
* @return the authenticator to be passed into the SDK.
*/
protected Authenticator authenticator() {
return PasswordAuthenticator.create(getUserName(), getPassword());
}
/**
* The {@link CouchbaseClientFactory} provides access to the lower level SDK resources.
*
* @param couchbaseCluster the cluster reference from the SDK.
* @return the initialized factory.
*/
@Bean(name = BeanNames.COUCHBASE_CLIENT_FACTORY)
public CouchbaseClientFactory couchbaseClientFactory(final Cluster couchbaseCluster) {
return new SimpleCouchbaseClientFactory(couchbaseCluster, getBucketName(), getScopeName());
}
@Bean(destroyMethod = "disconnect")
public Cluster couchbaseCluster(ClusterEnvironment couchbaseClusterEnvironment) {
return Cluster.connect(getConnectionString(),
clusterOptions(authenticator()).environment(couchbaseClusterEnvironment));
}
@Bean(destroyMethod = "shutdown")
public ClusterEnvironment couchbaseClusterEnvironment() {
ClusterEnvironment.Builder builder = ClusterEnvironment.builder();
if (!nonShadowedJacksonPresent()) {
throw new CouchbaseException("non-shadowed Jackson not present");
}
builder.jsonSerializer(JacksonJsonSerializer.create(couchbaseObjectMapper()));
configureEnvironment(builder);
return builder.build();
}
/**
* Can be overridden to customize the configuration of the environment before bootstrap.
*
* @param builder the builder that can be customized.
*/
protected void configureEnvironment(final ClusterEnvironment.Builder builder) {
}
@Bean(name = BeanNames.COUCHBASE_TEMPLATE)
public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) {
return new CouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, couchbaseTranslationService,
getDefaultConsistency());
}
public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
MappingCouchbaseConverter mappingCouchbaseConverter) {
return couchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, new JacksonTranslationService());
}
@Bean(name = BeanNames.REACTIVE_COUCHBASE_TEMPLATE)
public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) {
return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, couchbaseTranslationService,
getDefaultConsistency());
}
public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
MappingCouchbaseConverter mappingCouchbaseConverter) {
return reactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter,
new JacksonTranslationService());
}
@Bean(name = BeanNames.COUCHBASE_OPERATIONS_MAPPING)
public RepositoryOperationsMapping couchbaseRepositoryOperationsMapping(CouchbaseTemplate couchbaseTemplate) {
// create a base mapping that associates all repositories to the default template
RepositoryOperationsMapping baseMapping = new RepositoryOperationsMapping(couchbaseTemplate);
// let the user tune it
configureRepositoryOperationsMapping(baseMapping);
return baseMapping;
}
/**
* In order to customize the mapping between repositories/entity types to couchbase templates, use the provided
* mapping's api (eg. in order to have different buckets backing different repositories).
*
* @param mapping the default mapping (will associate all repositories to the default template).
*/
protected void configureRepositoryOperationsMapping(RepositoryOperationsMapping mapping) {
// NO_OP
}
@Bean(name = BeanNames.REACTIVE_COUCHBASE_OPERATIONS_MAPPING)
public ReactiveRepositoryOperationsMapping reactiveCouchbaseRepositoryOperationsMapping(
ReactiveCouchbaseTemplate reactiveCouchbaseTemplate) {
// create a base mapping that associates all repositories to the default template
ReactiveRepositoryOperationsMapping baseMapping = new ReactiveRepositoryOperationsMapping(
reactiveCouchbaseTemplate);
// let the user tune it
configureReactiveRepositoryOperationsMapping(baseMapping);
return baseMapping;
}
/**
* In order to customize the mapping between repositories/entity types to couchbase templates, use the provided
* mapping's api (eg. in order to have different buckets backing different repositories).
*
* @param mapping the default mapping (will associate all repositories to the default template).
*/
protected void configureReactiveRepositoryOperationsMapping(ReactiveRepositoryOperationsMapping mapping) {
// NO_OP
}
/**
* Scans the mapping base package for classes annotated with {@link Document}.
*
* @throws ClassNotFoundException if initial entity sets could not be loaded.
*/
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getMappingBasePackage();
Set<Class<?>> initialEntitySet = new HashSet<>();
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(
ClassUtils.forName(candidate.getBeanClassName(), AbstractCouchbaseConfiguration.class.getClassLoader()));
}
}
return initialEntitySet;
}
/**
* Determines the name of the field that will store the type information for complex types when using the
* {@link #mappingCouchbaseConverter(CouchbaseMappingContext, CouchbaseCustomConversions)}. Defaults to
* {@value MappingCouchbaseConverter#TYPEKEY_DEFAULT}.
*
* @see MappingCouchbaseConverter#TYPEKEY_DEFAULT
* @see MappingCouchbaseConverter#TYPEKEY_SYNCGATEWAY_COMPATIBLE
*/
public String typeKey() {
return MappingCouchbaseConverter.TYPEKEY_DEFAULT;
}
/**
* Creates a {@link MappingCouchbaseConverter} using the configured {@link #couchbaseMappingContext}.
*/
@Bean
public MappingCouchbaseConverter mappingCouchbaseConverter(CouchbaseMappingContext couchbaseMappingContext,
CouchbaseCustomConversions couchbaseCustomConversions) {
MappingCouchbaseConverter converter = new MappingCouchbaseConverter(couchbaseMappingContext, typeKey());
converter.setCustomConversions(couchbaseCustomConversions);
return converter;
}
/**
* Creates a {@link TranslationService}.
*
* @return TranslationService, defaulting to JacksonTranslationService.
*/
@Bean
public TranslationService couchbaseTranslationService() {
final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService();
jacksonTranslationService.afterPropertiesSet();
// for sdk3, we need to ask the mapper _it_ uses to ignore extra fields...
JacksonTransformers.MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return jacksonTranslationService;
}
/**
* Creates a {@link CouchbaseMappingContext} equipped with entity classes scanned from the mapping base package.
*/
@Bean(BeanNames.COUCHBASE_MAPPING_CONTEXT)
public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customConversions) throws Exception {
CouchbaseMappingContext mappingContext = new CouchbaseMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
mappingContext.setAutoIndexCreation(autoIndexCreation());
return mappingContext;
}
/**
* Creates a {@link ObjectMapper} for the jsonSerializer of the ClusterEnvironment
*
* @return ObjectMapper
*/
public ObjectMapper couchbaseObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new JsonValueModule());
CryptoManager cryptoManager = null;
if (cryptoManager != null) {
mapper.registerModule(new EncryptionModule(cryptoManager));
}
return mapper;
}
/**
* The default blocking transaction manager. It is an implementation of CallbackPreferringTransactionManager
* CallbackPreferrringTransactionmanagers do not play well with test-cases that rely
* on @TestTransaction/@BeforeTransaction/@AfterTransaction
*
* @param clientFactory
* @return
*/
@Bean(BeanNames.COUCHBASE_TRANSACTION_MANAGER)
CouchbaseCallbackTransactionManager couchbaseTransactionManager(CouchbaseClientFactory clientFactory) {
return new CouchbaseCallbackTransactionManager(clientFactory);
}
/**
* The default transaction template manager.
*
* @param couchbaseTransactionManager
* @return
*/
@Bean(BeanNames.COUCHBASE_TRANSACTION_TEMPLATE)
TransactionTemplate couchbaseTransactionTemplate(CouchbaseCallbackTransactionManager couchbaseTransactionManager) {
return new TransactionTemplate(couchbaseTransactionManager);
}
/**
* The default TransactionalOperator.
*
* @param couchbaseCallbackTransactionManager
* @return
*/
@Bean(BeanNames.COUCHBASE_TRANSACTIONAL_OPERATOR)
public CouchbaseTransactionalOperator couchbaseTransactionalOperator(
CouchbaseCallbackTransactionManager couchbaseCallbackTransactionManager) {
return CouchbaseTransactionalOperator.create(couchbaseCallbackTransactionManager);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionManager couchbaseTransactionManager) {
TransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
TransactionInterceptor interceptor = new CouchbaseTransactionInterceptor(couchbaseTransactionManager,
transactionAttributeSource);
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (couchbaseTransactionManager != null) {
interceptor.setTransactionManager(couchbaseTransactionManager);
}
return interceptor;
}
/**
* Configure whether to automatically create indices for domain types by deriving the from the entity or not.
*/
protected boolean autoIndexCreation() {
return false;
}
/**
* Register custom Converters in a {@link CustomConversions} object if required. These {@link CustomConversions} will
* be registered with the {@link #mappingCouchbaseConverter(CouchbaseMappingContext, CouchbaseCustomConversions)} )}
* and {@link #couchbaseMappingContext(CustomConversions)}. Returns an empty {@link CustomConversions} instance by
* default.
*
* @return must not be {@literal null}.
*/
@Bean(name = BeanNames.COUCHBASE_CUSTOM_CONVERSIONS)
public CustomConversions customConversions() {
return new CouchbaseCustomConversions(Collections.emptyList());
}
/**
* Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration
* class (the concrete class, not this one here) by default.
* <p>
* So if you have a {@code com.acme.AppConfig} extending {@link AbstractCouchbaseConfiguration} the base package will
* be considered {@code com.acme} unless the method is overridden to implement alternate behavior.
*
* @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for
* entities.
*/
protected String getMappingBasePackage() {
return getClass().getPackage().getName();
}
/**
* Set to true if field names should be abbreviated with the {@link CamelCaseAbbreviatingFieldNamingStrategy}.
*
* @return true if field names should be abbreviated, default is false.
*/
protected boolean abbreviateFieldNames() {
return false;
}
/**
* Configures a {@link FieldNamingStrategy} on the {@link CouchbaseMappingContext} instance created.
*
* @return the naming strategy.
*/
protected FieldNamingStrategy fieldNamingStrategy() {
return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy()
: PropertyNameFieldNamingStrategy.INSTANCE;
}
private boolean nonShadowedJacksonPresent() {
try {
JacksonJsonSerializer.preflightCheck();
return true;
} catch (Throwable t) {
return false;
}
}
public QueryScanConsistency getDefaultConsistency() {
return null;
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.config;
/**
* Contains default bean names for Couchbase beans. These are the names of the beans used by Spring Data Couchbase,
* unless an explicit id is given to the bean either in the xml configuration or the
* {@link AbstractCouchbaseConfiguration java configuration}.
*
* @author Michael Nitschinger
* @author Simon Baslé
* @author Michael Reiche
* @author Jorge Rodríguez Martín
*/
public class BeanNames {
public static final String COUCHBASE_TEMPLATE = "couchbaseTemplate";
public static final String REACTIVE_COUCHBASE_TEMPLATE = "reactiveCouchbaseTemplate";
public static final String COUCHBASE_CUSTOM_CONVERSIONS = "couchbaseCustomConversions";
/**
* The name for the bean that stores custom mapping between repositories and their backing couchbaseOperations.
*/
public static final String COUCHBASE_OPERATIONS_MAPPING = "couchbaseRepositoryOperationsMapping";
/**
* The name for the bean that stores custom mapping between reactive repositories and their backing
* reactiveCouchbaseOperations.
*/
public static final String REACTIVE_COUCHBASE_OPERATIONS_MAPPING = "reactiveCouchbaseRepositoryOperationsMapping";
/**
* The name for the bean that stores mapping metadata for entities stored in couchbase.
*/
public static final String COUCHBASE_MAPPING_CONTEXT = "couchbaseMappingContext";
/**
* The name for the bean that will handle audit trail marking of entities.
*/
public static final String COUCHBASE_AUDITING_HANDLER = "couchbaseAuditingHandler";
/**
* The name for the bean that will handle reactive audit trail marking of entities.
*/
public static final String REACTIVE_COUCHBASE_AUDITING_HANDLER = "reactiveCouchbaseAuditingHandler";
public static final String COUCHBASE_CLIENT_FACTORY = "couchbaseClientFactory";
public static final String COUCHBASE_TRANSACTION_MANAGER = "couchbaseTransactionManager";
public static final String COUCHBASE_TRANSACTION_TEMPLATE = "couchbaseTransactionTemplate";
public static final String COUCHBASE_TRANSACTIONAL_OPERATOR = "couchbaseTransactionalOperator";
}

View File

@@ -1,4 +0,0 @@
/**
* This package contains all classes needed for specific configuration of Spring Data Couchbase.
*/
package org.springframework.data.couchbase.config;

View File

@@ -1,234 +0,0 @@
/*
* Copyright 2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.lang.reflect.InaccessibleObjectException;
import java.util.Map;
import java.util.Set;
import com.couchbase.client.core.annotation.Stability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.join.N1qlJoinResolver;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.event.AfterSaveEvent;
import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent;
import org.springframework.data.couchbase.core.support.TemplateUtils;
import org.springframework.data.couchbase.repository.support.MappingCouchbaseEntityInformation;
import org.springframework.data.couchbase.transaction.CouchbaseResourceHolder;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.util.ClassUtils;
import com.couchbase.client.core.error.CouchbaseException;
/**
* Base shared by Reactive and non-Reactive TemplateSupport
*
* @author Michael Reiche
*/
@Stability.Internal
public abstract class AbstractTemplateSupport {
final ReactiveCouchbaseTemplate template;
final CouchbaseConverter converter;
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
final TranslationService translationService;
ApplicationContext applicationContext;
static final Logger LOG = LoggerFactory.getLogger(AbstractTemplateSupport.class);
public AbstractTemplateSupport(ReactiveCouchbaseTemplate template, CouchbaseConverter converter,
TranslationService translationService) {
this.template = template;
this.converter = converter;
this.mappingContext = converter.getMappingContext();
this.translationService = translationService;
}
abstract ReactiveCouchbaseTemplate getReactiveTemplate();
public <T> T decodeEntityBase(String id, String source, Long cas, Class<T> entityClass, String scope,
String collection, Object txResultHolder, CouchbaseResourceHolder holder) {
// this is the entity class defined for the repository. It may not be the class of the document that was read
// we will reset it after reading the document
//
// This will fail for the case where:
// 1) The version is defined in the concrete class, but not in the abstract class; and
// 2) The constructor takes a "long version" argument resulting in an exception would be thrown if version in
// the source is null.
// We could expose from the MappingCouchbaseConverter determining the persistent entity from the source,
// but that is a lot of work to do every time just for this very rare and avoidable case.
// TypeInformation<? extends R> typeToUse = typeMapper.readType(source, type);
CouchbasePersistentEntity persistentEntity = couldBePersistentEntity(entityClass);
if (persistentEntity == null) { // method could return a Long, Boolean, String etc.
// QueryExecutionConverters.unwrapWrapperTypes will recursively unwrap until there is nothing left
// to unwrap. This results in List<String[]> being unwrapped past String[] to String, so this may also be a
// Collection (or Array) of entityClass. We have no way of knowing - so just assume it is what we are told.
// if this is a Collection or array, only the first element will be returned.
final CouchbaseDocument converted = new CouchbaseDocument(id);
Set<Map.Entry<String, Object>> set = ((CouchbaseDocument) translationService.decode(source, converted))
.getContent().entrySet();
return (T) set.iterator().next().getValue();
}
if (id == null) {
throw new CouchbaseException(TemplateUtils.SELECT_ID + " was null. Either use #{#n1ql.selectEntity} or project "
+ TemplateUtils.SELECT_ID);
}
final CouchbaseDocument converted = new CouchbaseDocument(id);
// if possible, set the version property in the source so that if the constructor has a long version argument,
// it will have a value and not fail (as null is not a valid argument for a long argument). This possible failure
// can be avoid by defining the argument as Long instead of long.
// persistentEntity is still the (possibly abstract) class specified in the repository definition
// it's possible that the abstract class does not have a version property, and this won't be able to set the version
if (persistentEntity.getVersionProperty() != null) {
if (cas == null) {
throw new CouchbaseException("version/cas in the entity but " + TemplateUtils.SELECT_CAS
+ " was not in result. Either use #{#n1ql.selectEntity} or project " + TemplateUtils.SELECT_CAS);
}
if (cas != 0) {
converted.put(persistentEntity.getVersionProperty().getName(), cas);
}
}
// if the constructor has an argument that is long version, then construction will fail if the 'version'
// is not available as 'null' is not a legal value for a long. Changing the arg to "Long version" would solve this.
// (Version doesn't come from 'source', it comes from the cas argument to decodeEntity)
T readEntity = converter.read(entityClass, (CouchbaseDocument) translationService.decode(source, converted));
final ConvertingPropertyAccessor<T> accessor = getPropertyAccessor(readEntity);
persistentEntity = couldBePersistentEntity(readEntity.getClass());
if (cas != null && cas != 0 && persistentEntity.getVersionProperty() != null) {
accessor.setProperty(persistentEntity.getVersionProperty(), cas);
}
N1qlJoinResolver.handleProperties(persistentEntity, accessor, getReactiveTemplate(), id, scope, collection);
if (holder != null) {
holder.transactionResultHolder(txResultHolder, (T) accessor.getBean());
}
return accessor.getBean();
}
CouchbasePersistentEntity couldBePersistentEntity(Class<?> entityClass) {
if (ClassUtils.isPrimitiveOrWrapper(entityClass) || entityClass == String.class) {
return null;
}
try {
return mappingContext.getPersistentEntity(entityClass);
} catch (InaccessibleObjectException t) {
}
return null;
}
public <T> T applyResultBase(T entity, CouchbaseDocument converted, Object id, long cas,
Object txResultHolder, CouchbaseResourceHolder holder) {
ConvertingPropertyAccessor<Object> accessor = getPropertyAccessor(entity);
final CouchbasePersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(entity.getClass());
final CouchbasePersistentProperty idProperty = persistentEntity.getIdProperty();
if (idProperty != null) {
accessor.setProperty(idProperty, id);
}
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
if (versionProperty != null) {
accessor.setProperty(versionProperty, cas);
}
if (holder != null) {
holder.transactionResultHolder(txResultHolder, (T) accessor.getBean());
}
maybeEmitEvent(new AfterSaveEvent(accessor.getBean(), converted));
return (T) accessor.getBean();
}
public Long getCas(final Object entity) {
final ConvertingPropertyAccessor<Object> accessor = getPropertyAccessor(entity);
final CouchbasePersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(entity.getClass());
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
long cas = 0;
if (versionProperty != null) {
Object casObject = accessor.getProperty(versionProperty);
if (casObject instanceof Number) {
cas = ((Number) casObject).longValue();
}
}
return cas;
}
public Object getId(final Object entity) {
final ConvertingPropertyAccessor<Object> accessor = getPropertyAccessor(entity);
final CouchbasePersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(entity.getClass());
final CouchbasePersistentProperty idProperty = persistentEntity.getIdProperty();
Object id = null;
if (idProperty != null) {
id = accessor.getProperty(idProperty);
}
return id;
}
public String getJavaNameForEntity(final Class<?> clazz) {
final CouchbasePersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz);
MappingCouchbaseEntityInformation<?, Object> info = new MappingCouchbaseEntityInformation<>(persistentEntity);
return info.getJavaType().getName();
}
<T> ConvertingPropertyAccessor<T> getPropertyAccessor(final T source) {
CouchbasePersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(source.getClass());
PersistentPropertyAccessor<T> accessor = entity.getPropertyAccessor(source);
return new ConvertingPropertyAccessor<>(accessor, converter.getConversionService());
}
public void maybeEmitEvent(CouchbaseMappingEvent<?> event) {
if (canPublishEvent()) {
try {
this.applicationContext.publishEvent(event);
} catch (Exception e) {
LOG.warn("{} thrown during {}", e, event);
throw e;
}
} else {
LOG.info("maybeEmitEvent called, but CouchbaseTemplate not initialized with applicationContext");
}
}
private boolean canPublishEvent() {
return this.applicationContext != null;
}
public TranslationService getTranslationService() {
return translationService;
}
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/**
* Defines the callback which will be wrapped and executed on a bucket.
*
* @author Michael Nitschinger
*/
public interface CollectionCallback<T> {
/**
* The enclosed body will be executed on the connected bucket.
*
* @return the result of the enclosed execution.
* @throws TimeoutException if the enclosed operation timed out.
* @throws ExecutionException if the result could not be retrieved because of a thrown exception before.
* @throws InterruptedException if the enclosed operation was interrupted.
*/
T doInCollection() throws TimeoutException, ExecutionException, InterruptedException;
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.dao.DataIntegrityViolationException;
/**
* A Couchbase specific integrity violation exception, thrown as a result of failing db operations.
*
* @author Michael Nitschinger
*/
public class CouchbaseDataIntegrityViolationException extends DataIntegrityViolationException {
private static final long serialVersionUID = -3724991479213025850L;
public CouchbaseDataIntegrityViolationException(String msg) {
super(msg);
}
public CouchbaseDataIntegrityViolationException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,134 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.ConcurrentModificationException;
import java.util.concurrent.TimeoutException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.couchbase.transaction.error.UncategorizedTransactionDataAccessException;
import com.couchbase.client.core.error.BucketNotFoundException;
import com.couchbase.client.core.error.CasMismatchException;
import com.couchbase.client.core.error.CollectionNotFoundException;
import com.couchbase.client.core.error.ConfigException;
import com.couchbase.client.core.error.DecodingFailureException;
import com.couchbase.client.core.error.DesignDocumentNotFoundException;
import com.couchbase.client.core.error.DocumentExistsException;
import com.couchbase.client.core.error.DocumentLockedException;
import com.couchbase.client.core.error.DocumentNotFoundException;
import com.couchbase.client.core.error.DurabilityAmbiguousException;
import com.couchbase.client.core.error.DurabilityImpossibleException;
import com.couchbase.client.core.error.DurabilityLevelNotAvailableException;
import com.couchbase.client.core.error.EncodingFailureException;
import com.couchbase.client.core.error.ReplicaNotConfiguredException;
import com.couchbase.client.core.error.RequestCanceledException;
import com.couchbase.client.core.error.ScopeNotFoundException;
import com.couchbase.client.core.error.ServiceNotAvailableException;
import com.couchbase.client.core.error.TemporaryFailureException;
import com.couchbase.client.core.error.ValueTooLargeException;
import com.couchbase.client.core.error.transaction.TransactionOperationFailedException;
/**
* Simple {@link PersistenceExceptionTranslator} for Couchbase.
* <p>
* Convert the given runtime exception to an appropriate exception from the {@code org.springframework.dao} hierarchy.
* Return {@literal null} if no translation is appropriate: any other exception may have resulted from user code, and
* should not be translated.
*
* @author Michael Nitschinger
* @author Simon Baslé
* @author Michael Reiche
* @author Graham Pople
*/
public class CouchbaseExceptionTranslator implements PersistenceExceptionTranslator {
/**
* Translate Couchbase specific exceptions to spring exceptions if possible.
*
* @param ex the exception to translate.
* @return the translated exception or null.
*/
@Override
public final DataAccessException translateExceptionIfPossible(final RuntimeException ex) {
if (ex instanceof ConfigException || ex instanceof ServiceNotAvailableException
|| ex instanceof CollectionNotFoundException || ex instanceof ScopeNotFoundException
|| ex instanceof BucketNotFoundException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof DocumentExistsException) {
return new DuplicateKeyException(ex.getMessage(), ex);
}
if (ex instanceof DocumentNotFoundException) {
return new DataRetrievalFailureException(ex.getMessage(), ex);
}
if (ex instanceof CasMismatchException || ex instanceof ConcurrentModificationException) {
return new OptimisticLockingFailureException(ex.getMessage(), ex);
}
if (ex instanceof ReplicaNotConfiguredException || ex instanceof DurabilityLevelNotAvailableException
|| ex instanceof DurabilityImpossibleException || ex instanceof DurabilityAmbiguousException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (ex instanceof RequestCanceledException) {
return new OperationCancellationException(ex.getMessage(), ex);
}
if (ex instanceof DesignDocumentNotFoundException || ex instanceof ValueTooLargeException) {
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
}
if (ex instanceof TemporaryFailureException || ex instanceof DocumentLockedException) {
return new TransientDataAccessResourceException(ex.getMessage(), ex);
}
if ((ex instanceof RuntimeException && ex.getCause() instanceof TimeoutException)) {
return new QueryTimeoutException(ex.getMessage(), ex);
}
if (ex instanceof EncodingFailureException || ex instanceof DecodingFailureException) {
// note: the more specific CouchbaseQueryExecutionException should be thrown by the template
// when dealing with TranscodingException in the query/n1ql methods.
return new DataRetrievalFailureException(ex.getMessage(), ex);
}
if (ex instanceof TransactionOperationFailedException) {
// Replace the TransactionOperationFailedException, since we want the Spring operation to fail with a
// Spring error. Internal state has already been set in the AttemptContext so the retry, rollback etc.
// will get respected regardless of what gets propagated (or not) from the lambda.
return new UncategorizedTransactionDataAccessException((TransactionOperationFailedException) ex);
}
// Unable to translate exception, therefore just throw the original!
throw ex;
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.query.Query;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* Defines common operations on the Couchbase data source, most commonly implemented by {@link CouchbaseTemplate}.
*
* @author Michael Reiche
*/
public interface CouchbaseOperations extends FluentCouchbaseOperations {
/**
* Returns the converter used for this template/operations.
*/
CouchbaseConverter getConverter();
/**
* The name of the bucket used.
*/
String getBucketName();
/**
* The name of the scope used, null if the default scope is used.
*/
String getScopeName();
/**
* Returns the underlying client factory.
*/
CouchbaseClientFactory getCouchbaseClientFactory();
/**
* Returns the default consistency to use for queries
*/
QueryScanConsistency getConsistency();
/**
* Save the entity to couchbase.<br>
* If there is no version property on the entity class, and this is in a transaction, use insert. <br>
* If there is no version property on the entity class, and this is not in a transaction, use upsert. <br>
* If there is a version property on the entity class, and it is non-zero, then this is an existing document, use
* replace.<br>
* Otherwise, there is a version property for the entity, but it is zero or null, use insert. <br>
*
* @param entity the entity to save in couchbase
* @param scopeAndCollection for use by repositories only. these are varargs for the scope and collection.
* @param <T> the entity class
* @return
*/
<T> T save(T entity, String... scopeAndCollection);
/**
* Returns the count of documents found by the query.
* @param query
* @param domainType
* @param <T>
* @return
*/
<T> Long count(Query query, Class<T> domainType);
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.dao.DataRetrievalFailureException;
/**
* An {@link DataRetrievalFailureException} that denotes an error during a query (N1QL).
*/
public class CouchbaseQueryExecutionException extends DataRetrievalFailureException {
public CouchbaseQueryExecutionException(String msg) {
super(msg);
}
public CouchbaseQueryExecutionException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,224 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.lang.Nullable;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* Implements lower-level couchbase operations on top of the SDK with entity mapping capabilities.
*
* @author Michael Nitschinger
* @author Michael Reiche
* @author Jorge Rodriguez Martin
* @since 3.0
*/
public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContextAware {
private final CouchbaseClientFactory clientFactory;
private final CouchbaseConverter converter;
private final CouchbaseTemplateSupport templateSupport;
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
private final ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
private final QueryScanConsistency scanConsistency;
private @Nullable CouchbasePersistentEntityIndexCreator indexCreator;
public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) {
this(clientFactory, converter, new JacksonTranslationService());
}
public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
final TranslationService translationService) {
this(clientFactory, converter, translationService, null);
}
public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
final TranslationService translationService, QueryScanConsistency scanConsistency) {
this.clientFactory = clientFactory;
this.converter = converter;
this.templateSupport = new CouchbaseTemplateSupport(this, converter, translationService);
this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter, translationService,
scanConsistency);
this.scanConsistency = scanConsistency;
this.mappingContext = this.converter.getMappingContext();
if (mappingContext instanceof CouchbaseMappingContext) {
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
if (cmc.isAutoIndexCreation()) {
indexCreator = new CouchbasePersistentEntityIndexCreator(cmc, this);
}
}
}
@Override
public <T> T save(T entity, String... scopeAndCollection) {
return reactive().save(entity, scopeAndCollection).block();
}
@Override
public <T> Long count(Query query, Class<T> domainType) {
return findByQuery(domainType).matching(query).count();
}
@Override
public <T> ExecutableUpsertById<T> upsertById(final Class<T> domainType) {
return new ExecutableUpsertByIdOperationSupport(this).upsertById(domainType);
}
@Override
public <T> ExecutableInsertById<T> insertById(Class<T> domainType) {
return new ExecutableInsertByIdOperationSupport(this).insertById(domainType);
}
@Override
public <T> ExecutableReplaceById<T> replaceById(Class<T> domainType) {
return new ExecutableReplaceByIdOperationSupport(this).replaceById(domainType);
}
@Override
public <T> ExecutableFindById<T> findById(Class<T> domainType) {
return new ExecutableFindByIdOperationSupport(this).findById(domainType);
}
@Override
public <T> ExecutableFindFromReplicasById<T> findFromReplicasById(Class<T> domainType) {
return new ExecutableFindFromReplicasByIdOperationSupport(this).findFromReplicasById(domainType);
}
@Override
public <T> ExecutableFindByQuery<T> findByQuery(Class<T> domainType) {
return new ExecutableFindByQueryOperationSupport(this).findByQuery(domainType);
}
@Override
public <T> ExecutableFindByAnalytics<T> findByAnalytics(Class<T> domainType) {
return new ExecutableFindByAnalyticsOperationSupport(this).findByAnalytics(domainType);
}
@Override
@Deprecated
public ExecutableRemoveById removeById() {
return removeById(null);
}
@Override
public ExecutableRemoveById removeById(Class<?> domainType) {
return new ExecutableRemoveByIdOperationSupport(this).removeById(domainType);
}
@Override
@Deprecated
public ExecutableExistsById existsById() {
return existsById(null);
}
@Override
public ExecutableExistsById existsById(Class<?> domainType) {
return new ExecutableExistsByIdOperationSupport(this).existsById(domainType);
}
@Override
public <T> ExecutableRemoveByQuery<T> removeByQuery(Class<T> domainType) {
return new ExecutableRemoveByQueryOperationSupport(this).removeByQuery(domainType);
}
@Override
public String getBucketName() {
return clientFactory.getBucket().name();
}
@Override
public String getScopeName() {
return clientFactory.getScope().name();
}
@Override
public CouchbaseClientFactory getCouchbaseClientFactory() {
return clientFactory;
}
@Override
public QueryScanConsistency getConsistency() {
return scanConsistency;
}
/**
* Provides access to a {@link Collection} on the configured {@link CouchbaseClientFactory}.
*
* @param collectionName the name of the collection, if null is passed in the default collection is assumed.
* @return the collection instance.
*/
public Collection getCollection(final String collectionName) {
return clientFactory.getCollection(collectionName);
}
@Override
public CouchbaseConverter getConverter() {
return converter;
}
public ReactiveCouchbaseTemplate reactive() {
return reactiveCouchbaseTemplate;
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
prepareIndexCreator(applicationContext);
templateSupport.setApplicationContext(applicationContext);
reactiveCouchbaseTemplate.setApplicationContext(applicationContext);
}
private void prepareIndexCreator(final ApplicationContext context) {
String[] indexCreators = context.getBeanNamesForType(CouchbasePersistentEntityIndexCreator.class);
for (String creator : indexCreators) {
CouchbasePersistentEntityIndexCreator creatorBean = context.getBean(creator,
CouchbasePersistentEntityIndexCreator.class);
if (creatorBean.isIndexCreatorFor(mappingContext)) {
return;
}
}
if (context instanceof ConfigurableApplicationContext && indexCreator != null) {
((ConfigurableApplicationContext) context).addApplicationListener(indexCreator);
if (mappingContext instanceof CouchbaseMappingContext) {
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
cmc.setIndexCreator(indexCreator);
}
}
}
public TemplateSupport support() {
return templateSupport;
}
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.event.AfterConvertCallback;
import org.springframework.data.couchbase.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.couchbase.core.mapping.event.BeforeConvertEvent;
import org.springframework.data.couchbase.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.couchbase.transaction.CouchbaseResourceHolder;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.util.Assert;
/**
* Internal encode/decode support for CouchbaseTemplate.
*
* @author Michael Nitschinger
* @author Michael Reiche
* @author Jorge Rodriguez Martin
* @author Carlos Espinaco
* @since 3.0
*/
class CouchbaseTemplateSupport extends AbstractTemplateSupport implements ApplicationContextAware, TemplateSupport {
private final CouchbaseTemplate template;
private EntityCallbacks entityCallbacks;
public CouchbaseTemplateSupport(final CouchbaseTemplate template, final CouchbaseConverter converter,
final TranslationService translationService) {
super(template.reactive(), converter, translationService);
this.template = template;
}
@Override
public CouchbaseDocument encodeEntity(final Object entityToEncode) {
maybeEmitEvent(new BeforeConvertEvent<>(entityToEncode));
Object maybeNewEntity = maybeCallBeforeConvert(entityToEncode, "");
final CouchbaseDocument converted = new CouchbaseDocument();
converter.write(maybeNewEntity, converted);
maybeCallAfterConvert(entityToEncode, converted, "");
maybeEmitEvent(new BeforeSaveEvent<>(entityToEncode, converted));
return converted;
}
@Override
public <T> T decodeEntity(String id, String source, Long cas, Class<T> entityClass, String scope, String collection,
Object txHolder, CouchbaseResourceHolder holder) {
return decodeEntityBase(id, source, cas, entityClass, scope, collection, txHolder, holder);
}
@Override
public <T> T applyResult(T entity, CouchbaseDocument converted, Object id, long cas,
Object txResultHolder, CouchbaseResourceHolder holder) {
return applyResultBase(entity, converted, id, cas, txResultHolder, holder);
}
@Override
public <T> Integer getTxResultHolder(T source) {
return null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
if (entityCallbacks == null) {
setEntityCallbacks(EntityCallbacks.create(applicationContext));
}
}
/**
* Set the {@link EntityCallbacks} instance to use when invoking
* {@link org.springframework.data.mapping.callback.EntityCallback callbacks} like the {@link BeforeConvertCallback}.
* <p>
* Overrides potentially existing {@link EntityCallbacks}.
*
* @param entityCallbacks must not be {@literal null}.
* @throws IllegalArgumentException if the given instance is {@literal null}.
* @since 2.2
*/
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
Assert.notNull(entityCallbacks, "EntityCallbacks must not be null!");
this.entityCallbacks = entityCallbacks;
}
protected <T> T maybeCallBeforeConvert(T object, String collection) {
if (entityCallbacks != null) {
return entityCallbacks.callback(BeforeConvertCallback.class, object, collection);
} else {
LOG.info("maybeCallBeforeConvert called, but CouchbaseTemplate not initialized with applicationContext");
}
return object;
}
protected <T> T maybeCallAfterConvert(T object, CouchbaseDocument document, String collection) {
if (null != entityCallbacks) {
return entityCallbacks.callback(AfterConvertCallback.class, object, document, collection);
} else {
LOG.info("maybeCallAfterConvert called, but CouchbaseTemplate not initialized with applicationContext");
}
return object;
}
@Override
ReactiveCouchbaseTemplate getReactiveTemplate() {
return template.reactive();
}
}

View File

@@ -1,121 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.Collection;
import java.util.Map;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllExists;
import org.springframework.data.couchbase.core.support.WithExistsOptions;
import com.couchbase.client.java.kv.ExistsOptions;
/**
* Insert Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ExecutableExistsByIdOperation {
/**
* Checks if the document exists in the bucket.
*/
@Deprecated
ExecutableExistsById existsById();
/**
* Checks if the document exists in the bucket.
*/
ExecutableExistsById existsById(Class<?> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingExistsById extends OneAndAllExists {
/**
* Performs the operation on the ID given.
*
* @param id the ID to perform the operation on.
* @return true if the document exists, false otherwise.
*/
@Override
boolean one(String id);
/**
* Performs the operation on the collection of ids.
*
* @param ids the ids to check.
* @return a map consisting of the document IDs as the keys and if they exist as the value.
*/
@Override
Map<String, Boolean> all(Collection<String> ids);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface ExistsByIdWithOptions<T> extends TerminatingExistsById, WithExistsOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options options to use for execution
*/
@Override
TerminatingExistsById withOptions(ExistsOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface ExistsByIdInCollection<T> extends ExistsByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
ExistsByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface ExistsByIdInScope<T> extends ExistsByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
ExistsByIdInCollection<T> inScope(String scope);
}
/**
* Provides methods for constructing KV exists operations in a fluent way.
*/
interface ExecutableExistsById extends ExistsByIdInScope {}
}

View File

@@ -1,96 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.Collection;
import java.util.Map;
import org.springframework.data.couchbase.core.ReactiveExistsByIdOperationSupport.ReactiveExistsByIdSupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
import com.couchbase.client.java.kv.ExistsOptions;
public class ExecutableExistsByIdOperationSupport implements ExecutableExistsByIdOperation {
private final CouchbaseTemplate template;
ExecutableExistsByIdOperationSupport(CouchbaseTemplate template) {
this.template = template;
}
@Override
@Deprecated
public ExecutableExistsById existsById() {
return existsById(null);
}
@Override
public ExecutableExistsById existsById(Class<?> domainType) {
return new ExecutableExistsByIdSupport(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null);
}
static class ExecutableExistsByIdSupport implements ExecutableExistsById {
private final CouchbaseTemplate template;
private final Class<?> domainType;
private final String scope;
private final String collection;
private final ExistsOptions options;
private final ReactiveExistsByIdSupport reactiveSupport;
ExecutableExistsByIdSupport(final CouchbaseTemplate template, final Class<?> domainType, final String scope,
final String collection, final ExistsOptions options) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.reactiveSupport = new ReactiveExistsByIdSupport(template.reactive(), domainType, scope, collection, options);
}
@Override
public boolean one(final String id) {
return reactiveSupport.one(id).block();
}
@Override
public Map<String, Boolean> all(final Collection<String> ids) {
return reactiveSupport.all(ids).block();
}
@Override
public ExistsByIdWithOptions inCollection(final String collection) {
return new ExecutableExistsByIdSupport(template, domainType, scope,
collection != null ? collection : this.collection, options);
}
@Override
public TerminatingExistsById withOptions(final ExistsOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableExistsByIdSupport(template, domainType, scope, collection, options);
}
@Override
public ExistsByIdInCollection inScope(final String scope) {
return new ExecutableExistsByIdSupport(template, domainType, scope != null ? scope : this.scope, collection,
options);
}
}
}

View File

@@ -1,215 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAll;
import org.springframework.data.couchbase.core.support.WithAnalyticsConsistency;
import org.springframework.data.couchbase.core.support.WithAnalyticsOptions;
import org.springframework.data.couchbase.core.support.WithAnalyticsQuery;
import org.springframework.lang.Nullable;
import com.couchbase.client.java.analytics.AnalyticsOptions;
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
/**
* FindByAnalytics Operations
*
* @author Christoph Strobl
* @since 2.0
*/public interface ExecutableFindByAnalyticsOperation {
/**
* Queries the analytics service.
*
* @param domainType the entity type to use for the results.
*/
<T> ExecutableFindByAnalytics<T> findByAnalytics(Class<T> domainType);
interface TerminatingFindByAnalytics<T> extends OneAndAll<T> {
/**
* Get exactly zero or one result.
*
* @return {@link Optional#empty()} if no match found.
* @throws IncorrectResultSizeDataAccessException if more than one match found.
*/
default Optional<T> one() {
return Optional.ofNullable(oneValue());
}
/**
* Get exactly zero or one result.
*
* @return {@literal null} if no match found.
* @throws IncorrectResultSizeDataAccessException if more than one match found.
*/
@Nullable
T oneValue();
/**
* Get the first or no result.
*
* @return {@link Optional#empty()} if no match found.
*/
default Optional<T> first() {
return Optional.ofNullable(firstValue());
}
/**
* Get the first or no result.
*
* @return {@literal null} if no match found.
*/
@Nullable
T firstValue();
/**
* Get all matching elements.
*
* @return never {@literal null}.
*/
List<T> all();
/**
* Stream all matching elements.
*
* @return a {@link Stream} of results. Never {@literal null}.
*/
Stream<T> stream();
/**
* Get the number of matching elements.
*
* @return total number of matching elements.
*/
long count();
/**
* Check for the presence of matching elements.
*
* @return {@literal true} if at least one matching element exists.
*/
boolean exists();
}
interface FindByAnalyticsWithQuery<T> extends TerminatingFindByAnalytics<T>, WithAnalyticsQuery<T> {
/**
* Set the filter for the analytics query to be used.
*
* @param query must not be {@literal null}.
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingFindByAnalytics<T> matching(AnalyticsQuery query);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use.
*/
interface FindByAnalyticsWithOptions<T> extends FindByAnalyticsWithQuery<T>, WithAnalyticsOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
@Override
FindByAnalyticsWithQuery<T> withOptions(AnalyticsOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface FindByAnalyticsInCollection<T> extends FindByAnalyticsWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
FindByAnalyticsWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface FindByAnalyticsInScope<T> extends FindByAnalyticsInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
FindByAnalyticsInCollection<T> inScope(String scope);
}
@Deprecated
interface FindByAnalyticsConsistentWith<T> extends FindByAnalyticsInScope<T> {
/**
* Allows to override the default scan consistency.
*
* @param scanConsistency the custom scan consistency to use for this analytics query.
*/
@Deprecated
FindByAnalyticsWithQuery<T> consistentWith(AnalyticsScanConsistency scanConsistency);
}
interface FindByAnalyticsWithConsistency<T> extends FindByAnalyticsConsistentWith<T>, WithAnalyticsConsistency<T> {
/**
* Allows to override the default scan consistency.
*
* @param scanConsistency the custom scan consistency to use for this analytics query.
*/
FindByAnalyticsConsistentWith<T> withConsistency(AnalyticsScanConsistency scanConsistency);
}
/**
* Result type override (Optional).
*/
interface FindByAnalyticsWithProjection<T> extends FindByAnalyticsWithConsistency<T> {
/**
* Define the target type fields should be mapped to. <br />
* Skip this step if you are anyway only interested in the original domain type.
*
* @param returnType must not be {@literal null}.
* @return new instance of {@link FindByAnalyticsWithConsistency}.
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
<R> FindByAnalyticsWithConsistency<R> as(Class<R> returnType);
}
interface ExecutableFindByAnalytics<T> extends FindByAnalyticsWithProjection<T> {}
}

View File

@@ -1,149 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.data.couchbase.core.ReactiveFindByAnalyticsOperationSupport.ReactiveFindByAnalyticsSupport;
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
import com.couchbase.client.java.analytics.AnalyticsOptions;
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
public class ExecutableFindByAnalyticsOperationSupport implements ExecutableFindByAnalyticsOperation {
private static final AnalyticsQuery ALL_QUERY = new AnalyticsQuery();
private final CouchbaseTemplate template;
public ExecutableFindByAnalyticsOperationSupport(final CouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ExecutableFindByAnalytics<T> findByAnalytics(final Class<T> domainType) {
return new ExecutableFindByAnalyticsSupport<>(template, domainType, domainType, ALL_QUERY, null,
OptionsBuilder.getScopeFrom(domainType), OptionsBuilder.getCollectionFrom(domainType), null);
}
static class ExecutableFindByAnalyticsSupport<T> implements ExecutableFindByAnalytics<T> {
private final CouchbaseTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final ReactiveFindByAnalyticsSupport<T> reactiveSupport;
private final AnalyticsQuery query;
private final AnalyticsScanConsistency scanConsistency;
private final String scope;
private final String collection;
private final AnalyticsOptions options;
ExecutableFindByAnalyticsSupport(final CouchbaseTemplate template, final Class<?> domainType,
final Class<T> returnType, final AnalyticsQuery query, final AnalyticsScanConsistency scanConsistency,
final String scope, final String collection, final AnalyticsOptions options) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.query = query;
this.reactiveSupport = new ReactiveFindByAnalyticsSupport<>(template.reactive(), domainType, returnType, query,
scanConsistency, scope, collection, options, new NonReactiveSupportWrapper(template.support()));
this.scanConsistency = scanConsistency;
this.scope = scope;
this.collection = collection;
this.options = options;
}
@Override
public T oneValue() {
return reactiveSupport.one().block();
}
@Override
public T firstValue() {
return reactiveSupport.first().block();
}
@Override
public List<T> all() {
return reactiveSupport.all().collectList().block();
}
@Override
public TerminatingFindByAnalytics<T> matching(final AnalyticsQuery query) {
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options);
}
@Override
public FindByAnalyticsWithQuery<T> withOptions(final AnalyticsOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options);
}
@Override
public FindByAnalyticsInCollection<T> inScope(final String scope) {
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency,
scope != null ? scope : this.scope, collection, options);
}
@Override
public FindByAnalyticsWithConsistency<T> inCollection(final String collection) {
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection != null ? collection : this.collection, options);
}
@Override
@Deprecated
public FindByAnalyticsWithQuery<T> consistentWith(final AnalyticsScanConsistency scanConsistency) {
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options);
}
@Override
public FindByAnalyticsWithConsistency<T> withConsistency(final AnalyticsScanConsistency scanConsistency) {
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options);
}
@Override
public <R> FindByAnalyticsWithConsistency<R> as(final Class<R> returnType) {
Assert.notNull(returnType, "returnType must not be null!");
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options);
}
@Override
public Stream<T> stream() {
return reactiveSupport.all().toStream();
}
@Override
public long count() {
return reactiveSupport.count().block();
}
@Override
public boolean exists() {
return count() > 0;
}
}
}

View File

@@ -1,142 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.OneAndAllId;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.WithGetOptions;
import org.springframework.data.couchbase.core.support.WithProjectionId;
import org.springframework.data.couchbase.core.support.InScope;
import com.couchbase.client.java.kv.GetOptions;
import org.springframework.data.couchbase.core.support.WithExpiry;
/**
* Get Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ExecutableFindByIdOperation {
/**
* Loads a document from a bucket.
*
* @param domainType the entity type to use for the results.
*/
<T> ExecutableFindById<T> findById(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*
* @param <T> the entity type to use for the results.
*/
interface TerminatingFindById<T> extends OneAndAllId<T> {
/**
* Finds one document based on the given ID.
*
* @param id the document ID.
* @return the entity if found.
*/
T one(String id);
/**
* Finds a list of documents based on the given IDs.
*
* @param ids the document ID ids.
* @return the list of found entities.
*/
Collection<? extends T> all(Collection<String> ids);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface FindByIdWithOptions<T> extends TerminatingFindById<T>, WithGetOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options options to use for execution
*/
@Override
TerminatingFindById<T> withOptions(GetOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface FindByIdInCollection<T> extends FindByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
FindByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface FindByIdInScope<T> extends FindByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
FindByIdInCollection<T> inScope(String scope);
}
interface FindByIdWithProjection<T> extends FindByIdInScope<T>, WithProjectionId<T> {
/**
* Load only certain fields for the document.
*
* @param fields the projected fields to load.
*/
@Override
FindByIdInScope<T> project(String... fields);
}
interface FindByIdWithExpiry<T> extends FindByIdWithProjection<T>, WithExpiry<T> {
/**
* Load only certain fields for the document.
*
* @param expiry the projected fields to load.
*/
@Override
FindByIdWithProjection<T> withExpiry(Duration expiry);
}
/**
* Provides methods for constructing query operations in a fluent way.
*
* @param <T> the entity type to use for the results
*/
interface ExecutableFindById<T> extends FindByIdWithExpiry<T> {}
}

View File

@@ -1,107 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.data.couchbase.core.ReactiveFindByIdOperationSupport.ReactiveFindByIdSupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
import com.couchbase.client.java.kv.GetOptions;
public class ExecutableFindByIdOperationSupport implements ExecutableFindByIdOperation {
private final CouchbaseTemplate template;
ExecutableFindByIdOperationSupport(CouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ExecutableFindById<T> findById(Class<T> domainType) {
return new ExecutableFindByIdSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType),null, null, null);
}
static class ExecutableFindByIdSupport<T> implements ExecutableFindById<T> {
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final String scope;
private final String collection;
private final GetOptions options;
private final List<String> fields;
private final Duration expiry;
private final ReactiveFindByIdSupport<T> reactiveSupport;
ExecutableFindByIdSupport(CouchbaseTemplate template, Class<T> domainType, String scope, String collection,
GetOptions options, List<String> fields, Duration expiry) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.fields = fields;
this.expiry = expiry;
this.reactiveSupport = new ReactiveFindByIdSupport<>(template.reactive(), domainType, scope, collection, options,
fields, expiry, new NonReactiveSupportWrapper(template.support()));
}
@Override
public T one(final String id) {
return reactiveSupport.one(id).block();
}
@Override
public Collection<? extends T> all(final Collection<String> ids) {
return reactiveSupport.all(ids).collectList().block();
}
@Override
public TerminatingFindById<T> withOptions(final GetOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields, expiry);
}
@Override
public FindByIdWithOptions<T> inCollection(final String collection) {
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection != null ? collection : this.collection, options, fields, expiry);
}
@Override
public FindByIdInCollection<T> inScope(final String scope) {
return new ExecutableFindByIdSupport<>(template, domainType, scope != null ? scope : this.scope, collection, options, fields, expiry);
}
@Override
public FindByIdInScope<T> project(String... fields) {
Assert.notEmpty(fields, "Fields must not be null.");
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields), expiry);
}
@Override
public FindByIdWithProjection<T> withExpiry(final Duration expiry) {
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields,
expiry);
}
}
}

View File

@@ -1,300 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAll;
import org.springframework.data.couchbase.core.support.WithConsistency;
import org.springframework.data.couchbase.core.support.WithDistinct;
import org.springframework.data.couchbase.core.support.WithQuery;
import org.springframework.data.couchbase.core.support.WithQueryOptions;
import org.springframework.lang.Nullable;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* Query Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ExecutableFindByQueryOperation {
/**
* Queries the N1QL service.
*
* @param domainType the entity type to use for the results.
*/
<T> ExecutableFindByQuery<T> findByQuery(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingFindByQuery<T> extends OneAndAll<T> {
/**
* Get exactly zero or one result.
*
* @return {@link Optional#empty()} if no match found.
* @throws IncorrectResultSizeDataAccessException if more than one match found.
*/
@Override
default Optional<T> one() {
return Optional.ofNullable(oneValue());
}
/**
* Get exactly zero or one result.
*
* @return {@literal null} if no match found.
* @throws IncorrectResultSizeDataAccessException if more than one match found.
*/
@Nullable
@Override
T oneValue();
/**
* Get the first or no result.
*
* @return {@link Optional#empty()} if no match found.
*/
@Override
default Optional<T> first() {
return Optional.ofNullable(firstValue());
}
/**
* Get the first or no result.
*
* @return {@literal null} if no match found.
*/
@Nullable
@Override
T firstValue();
/**
* Get all matching documents.
*
* @return never {@literal null}.
*/
@Override
List<T> all();
/**
* Stream all matching elements.
*
* @return a {@link Stream} of results. Never {@literal null}.
*/
@Override
Stream<T> stream();
/**
* Get the number of matching elements.
*
* @return total number of matching elements.
*/
@Override
long count();
/**
* Check for the presence of matching elements.
*
* @return {@literal true} if at least one matching element exists.
*/
@Override
boolean exists();
}
/**
* Fluent methods to specify the query
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithQuery<T> extends TerminatingFindByQuery<T>, WithQuery<T> {
/**
* Set the filter for the query to be used.
*
* @param query must not be {@literal null}.
* @throws IllegalArgumentException if query is {@literal null}.
*/
@Override
TerminatingFindByQuery<T> matching(Query query);
/**
* Set the filter {@link QueryCriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link ExecutableFindByQuery}.
* @throws IllegalArgumentException if criteria is {@literal null}.
*/
@Override
default TerminatingFindByQuery<T> matching(QueryCriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithOptions<T> extends FindByQueryWithQuery<T>, WithQueryOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
@Override
TerminatingFindByQuery<T> withOptions(QueryOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryInCollection<T> extends FindByQueryWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
FindByQueryWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryInScope<T> extends FindByQueryInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
FindByQueryInCollection<T> inScope(String scope);
}
/**
* To be removed at the next major release. use WithConsistency instead
*
* @param <T> the entity type to use for the results.
*/
@Deprecated
interface FindByQueryConsistentWith<T> extends FindByQueryInScope<T> {
/**
* Allows to override the default scan consistency.
*
* @param scanConsistency the custom scan consistency to use for this query.
*/
@Deprecated
FindByQueryInScope<T> consistentWith(QueryScanConsistency scanConsistency);
}
/**
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithConsistency<T> extends FindByQueryConsistentWith<T>, WithConsistency<T> {
/**
* Allows to override the default scan consistency.
*
* @param scanConsistency the custom scan consistency to use for this query.
*/
@Override
FindByQueryConsistentWith<T> withConsistency(QueryScanConsistency scanConsistency);
}
/**
* Fluent method to specify a return type different than the the entity type to use for the results.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithProjection<T> extends FindByQueryWithConsistency<T> {
/**
* Define the target type fields should be mapped to. <br />
* Skip this step if you are only interested in the original the entity type to use for the results.
*
* @param returnType must not be {@literal null}.
* @return new instance of {@link FindByQueryWithProjection}.
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
<R> FindByQueryWithConsistency<R> as(Class<R> returnType);
}
/**
* Fluent method to specify fields to project.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {
/**
* Define the target type fields should be mapped to. <br />
* Skip this step if you are anyway only interested in the original domain type.
*
* @param fields to project
* @return new instance of {@link ReactiveFindByQueryOperation.FindByQueryWithConsistency}.
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
FindByQueryWithProjection<T> project(String[] fields);
}
/**
* Fluent method to specify DISTINCT fields
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithDistinct<T> {
/**
* Finds the distinct values for a specified {@literal field} across a single collection
*
* @param distinctFields name of the field. Must not be {@literal null}.
* @return new instance of {@link ExecutableFindByQuery}.
* @throws IllegalArgumentException if field is {@literal null}.
*/
@Override
FindByQueryWithProjection<T> distinct(String[] distinctFields);
}
/**
* Provides methods for constructing query operations in a fluent way.
*
* @param <T> the entity type to use for the results
*/
interface ExecutableFindByQuery<T> extends FindByQueryWithDistinct<T> {}
}

View File

@@ -1,190 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.util.Assert;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* {@link ExecutableFindByQueryOperation} implementations for Couchbase.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQueryOperation {
private static final Query ALL_QUERY = new Query();
private final CouchbaseTemplate template;
public ExecutableFindByQueryOperationSupport(final CouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ExecutableFindByQuery<T> findByQuery(final Class<T> domainType) {
return new ExecutableFindByQuerySupport<T>(template, domainType, domainType, ALL_QUERY, null,
OptionsBuilder.getScopeFrom(domainType), OptionsBuilder.getCollectionFrom(domainType), null, null, null);
}
static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T> {
private final CouchbaseTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final Query query;
private final ReactiveFindByQuerySupport<T> reactiveSupport;
private final QueryScanConsistency scanConsistency;
private final String scope;
private final String collection;
private final QueryOptions options;
private final String[] distinctFields;
private final String[] fields;
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<?> domainType, final Class<T> returnType,
final Query query, final QueryScanConsistency scanConsistency, final String scope, final String collection,
final QueryOptions options, final String[] distinctFields, final String[] fields) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.query = query;
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, returnType, query,
scanConsistency, scope, collection, options, distinctFields, fields,
new NonReactiveSupportWrapper(template.support()));
this.scanConsistency = scanConsistency;
this.scope = scope;
this.collection = collection;
this.options = options;
this.distinctFields = distinctFields;
this.fields = fields;
}
@Override
public T oneValue() {
return reactiveSupport.one().block();
}
@Override
public T firstValue() {
return reactiveSupport.first().block();
}
@Override
public List<T> all() {
return reactiveSupport.all().collectList().block();
}
@Override
public TerminatingFindByQuery<T> matching(final Query query) {
QueryScanConsistency scanCons;
if (query.getScanConsistency() != null) {
scanCons = query.getScanConsistency();
} else {
scanCons = scanConsistency;
}
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
options, distinctFields, fields);
}
@Override
@Deprecated
public FindByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields);
}
@Override
public FindByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields);
}
@Override
public <R> FindByQueryWithConsistency<R> as(final Class<R> returnType) {
Assert.notNull(returnType, "returnType must not be null!");
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields);
}
@Override
public FindByQueryWithProjection<T> project(String[] fields) {
Assert.notNull(fields, "Fields must not be null");
Assert.isNull(distinctFields, "only one of project(fields) and distinct(distinctFields) can be specified");
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields);
}
@Override
public FindByQueryWithProjection<T> distinct(final String[] distinctFields) {
Assert.notNull(distinctFields, "distinctFields must not be null");
Assert.isNull(fields, "only one of project(fields) and distinct(distinctFields) can be specified");
// Coming from an annotation, this cannot be null.
// But a non-null but empty distinctFields means distinct on all fields
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, dFields, fields);
}
@Override
public Stream<T> stream() {
return reactiveSupport.all().toStream();
}
@Override
public long count() {
Long l = reactiveSupport.count().block();
if (l == null) {
throw new CouchbaseQueryExecutionException("count query did not return a count : " + query.export());
}
return l;
}
@Override
public boolean exists() {
return count() > 0;
}
@Override
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields);
}
@Override
public FindByQueryInCollection<T> inScope(final String scope) {
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency,
scope != null ? scope : this.scope, collection, options, distinctFields, fields);
}
@Override
public FindByQueryWithConsistency<T> inCollection(final String collection) {
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection != null ? collection : this.collection, options, distinctFields, fields);
}
}
}

View File

@@ -1,117 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.AnyId;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.WithGetAnyReplicaOptions;
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
/**
* Query Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ExecutableFindFromReplicasByIdOperation {
/**
* Loads a document from a replica.
*
* @param domainType the entity type to use for the results.
*/
<T> ExecutableFindFromReplicasById<T> findFromReplicasById(Class<T> domainType);
/**
* Terminating operations invoking the actual get execution.
*/
interface TerminatingFindFromReplicasById<T> extends AnyId<T> {
/**
* Finds one document based on the given ID.
*
* @param id the document ID.
* @return the entity if found.
*/
@Override
T any(String id);
/**
* Finds a list of documents based on the given IDs.
*
* @param ids the document ID ids.
* @return the list of found entities.
*/
@Override
Collection<? extends T> any(Collection<String> ids);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface FindFromReplicasByIdWithOptions<T> extends TerminatingFindFromReplicasById<T>, WithGetAnyReplicaOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options options to use for execution
*/
@Override
TerminatingFindFromReplicasById<T> withOptions(GetAnyReplicaOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface FindFromReplicasByIdInCollection<T> extends FindFromReplicasByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
FindFromReplicasByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface FindFromReplicasByIdInScope<T> extends FindFromReplicasByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
FindFromReplicasByIdInCollection<T> inScope(String scope);
}
/**
* Provides methods for constructing get operations in a fluent way.
*
* @param <T> the entity type to use for the results
*/
interface ExecutableFindFromReplicasById<T> extends FindFromReplicasByIdInScope<T> {}
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.Collection;
import org.springframework.data.couchbase.core.ReactiveFindFromReplicasByIdOperationSupport.ReactiveFindFromReplicasByIdSupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
public class ExecutableFindFromReplicasByIdOperationSupport implements ExecutableFindFromReplicasByIdOperation {
private final CouchbaseTemplate template;
ExecutableFindFromReplicasByIdOperationSupport(CouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ExecutableFindFromReplicasById<T> findFromReplicasById(Class<T> domainType) {
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, domainType,
OptionsBuilder.getScopeFrom(domainType), OptionsBuilder.getCollectionFrom(domainType), null);
}
static class ExecutableFindFromReplicasByIdSupport<T> implements ExecutableFindFromReplicasById<T> {
private final CouchbaseTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final String scope;
private final String collection;
private final GetAnyReplicaOptions options;
private final ReactiveFindFromReplicasByIdSupport<T> reactiveSupport;
ExecutableFindFromReplicasByIdSupport(CouchbaseTemplate template, Class<?> domainType, Class<T> returnType,
String scope, String collection, GetAnyReplicaOptions options) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.returnType = returnType;
this.reactiveSupport = new ReactiveFindFromReplicasByIdSupport<>(template.reactive(), domainType, returnType,
scope, collection, options, new NonReactiveSupportWrapper(template.support()));
}
@Override
public T any(String id) {
return reactiveSupport.any(id).block();
}
@Override
public Collection<? extends T> any(Collection<String> ids) {
return reactiveSupport.any(ids).collectList().block();
}
@Override
public TerminatingFindFromReplicasById<T> withOptions(final GetAnyReplicaOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options);
}
@Override
public FindFromReplicasByIdWithOptions<T> inCollection(final String collection) {
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType, scope,
collection != null ? collection : this.collection, options);
}
@Override
public FindFromReplicasByIdInCollection<T> inScope(final String scope) {
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType,
scope != null ? scope : this.scope, collection, options);
}
}
}

View File

@@ -1,140 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllEntity;
import org.springframework.data.couchbase.core.support.WithDurability;
import org.springframework.data.couchbase.core.support.WithExpiry;
import org.springframework.data.couchbase.core.support.WithInsertOptions;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.InsertOptions;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* Insert Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ExecutableInsertByIdOperation {
/**
* Insert using the KV service.
*
* @param domainType the entity type to insert.
*/
<T> ExecutableInsertById<T> insertById(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingInsertById<T> extends OneAndAllEntity<T> {
/**
* Insert one entity.
*
* @return Inserted entity.
*/
@Override
T one(T object);
/**
* Insert a collection of entities.
*
* @return Inserted entities
*/
@Override
Collection<? extends T> all(Collection<? extends T> objects);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use.
*/
interface InsertByIdWithOptions<T>
extends TerminatingInsertById<T>, WithInsertOptions<T> {
/**
* Fluent method to specify options to use for execution.
*
* @param options to use for execution
*/
@Override
TerminatingInsertById<T> withOptions(InsertOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface InsertByIdInCollection<T> extends InsertByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
InsertByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface InsertByIdInScope<T> extends InsertByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
InsertByIdInCollection<T> inScope(String scope);
}
interface InsertByIdWithDurability<T> extends InsertByIdInScope<T>, WithDurability<T> {
@Override
InsertByIdInScope<T> withDurability(DurabilityLevel durabilityLevel);
@Override
InsertByIdInScope<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
}
interface InsertByIdWithExpiry<T> extends InsertByIdWithDurability<T>, WithExpiry<T> {
@Override
InsertByIdWithDurability<T> withExpiry(Duration expiry);
}
/**
* Provides methods for constructing KV insert operations in a fluent way.
*
* @param <T> the entity type to insert
*/
interface ExecutableInsertById<T> extends InsertByIdWithExpiry<T> {}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.ReactiveInsertByIdOperationSupport.ReactiveInsertByIdSupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.InsertOptions;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
public class ExecutableInsertByIdOperationSupport implements ExecutableInsertByIdOperation {
private final CouchbaseTemplate template;
public ExecutableInsertByIdOperationSupport(final CouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ExecutableInsertById<T> insertById(final Class<T> domainType) {
Assert.notNull(domainType, "DomainType must not be null!");
return new ExecutableInsertByIdSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
null);
}
static class ExecutableInsertByIdSupport<T> implements ExecutableInsertById<T> {
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final String scope;
private final String collection;
private final InsertOptions options;
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveInsertByIdSupport<T> reactiveSupport;
ExecutableInsertByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String scope,
final String collection, final InsertOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
final DurabilityLevel durabilityLevel, final Duration expiry) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.reactiveSupport = new ReactiveInsertByIdSupport<>(template.reactive(), domainType, scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
}
@Override
public T one(final T object) {
return reactiveSupport.one(object).block();
}
@Override
public Collection<? extends T> all(Collection<? extends T> objects) {
return reactiveSupport.all(objects).collectList().block();
}
@Override
public TerminatingInsertById<T> withOptions(final InsertOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry);
}
@Override
public InsertByIdInCollection<T> inScope(final String scope) {
return new ExecutableInsertByIdSupport<>(template, domainType, scope != null ? scope : this.scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry);
}
@Override
public InsertByIdWithOptions<T> inCollection(final String collection) {
return new ExecutableInsertByIdSupport<>(template, domainType, scope,
collection != null ? collection : this.collection, options, persistTo, replicateTo, durabilityLevel, expiry);
}
@Override
public InsertByIdInScope<T> withDurability(final DurabilityLevel durabilityLevel) {
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry);
}
@Override
public InsertByIdInScope<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
Assert.notNull(persistTo, "PersistTo must not be null.");
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry);
}
@Override
public InsertByIdWithDurability<T> withExpiry(final Duration expiry) {
Assert.notNull(expiry, "expiry must not be null.");
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry);
}
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.Collection;
import java.util.List;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllId;
import org.springframework.data.couchbase.core.support.WithDurability;
import org.springframework.data.couchbase.core.support.WithRemoveOptions;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.RemoveOptions;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* Remove Operations on KV service.
*
* @author Christoph Strobl
* @author Michael Reiche
* @since 2.0
*/
public interface ExecutableRemoveByIdOperation {
/**
* Removes a document.
*/
ExecutableRemoveById removeById(Class<?> domainType);
/**
* Removes a document.
*/
@Deprecated
ExecutableRemoveById removeById();
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingRemoveById extends OneAndAllId<RemoveResult> {
/**
* Remove one document based on the given ID.
*
* @param id the document ID.
* @return result of the remove
*/
@Override
RemoveResult one(String id);
/**
* Remove one document based on the entity. Transactions need the entity for the cas.
*
* @param entity the document ID.
* @return result of the remove
*/
RemoveResult oneEntity(Object entity);
/**
* Remove the documents in the collection.
*
* @param ids the document IDs.
* @return result of the removes.
*/
@Override
List<RemoveResult> all(Collection<String> ids);
/**
* Remove documents based on the entities. Transactions need the entity for the cas.
*
* @param entities to remove.
* @return result of the remove
*/
List<RemoveResult> allEntities(Collection<Object> entities);
}
/**
* Fluent method to specify options.
*/
interface RemoveByIdWithOptions extends TerminatingRemoveById, WithRemoveOptions<RemoveResult> {
/**
* Fluent method to specify options to use for execution
*
* @param options options to use for execution
*/
@Override
TerminatingRemoveById withOptions(RemoveOptions options);
}
/**
* Fluent method to specify the collection.
*/
interface RemoveByIdInCollection extends RemoveByIdWithOptions, InCollection<Object> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
RemoveByIdWithOptions inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*/
interface RemoveByIdInScope extends RemoveByIdInCollection, InScope<Object> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
RemoveByIdInCollection inScope(String scope);
}
interface RemoveByIdWithDurability extends RemoveByIdInScope, WithDurability<RemoveResult> {
@Override
RemoveByIdInScope withDurability(DurabilityLevel durabilityLevel);
@Override
RemoveByIdInScope withDurability(PersistTo persistTo, ReplicateTo replicateTo);
}
interface RemoveByIdWithCas extends RemoveByIdWithDurability {
RemoveByIdWithDurability withCas(Long cas);
}
/**
* Provides methods for constructing remove operations in a fluent way.
*/
interface ExecutableRemoveById extends RemoveByIdWithCas {}
}

View File

@@ -1,148 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.Collection;
import java.util.List;
import org.springframework.data.couchbase.core.ReactiveRemoveByIdOperationSupport.ReactiveRemoveByIdSupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.RemoveOptions;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* {@link ExecutableRemoveByIdOperation} implementations for Couchbase.
*
* @author Michael Reiche
*/
public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByIdOperation {
private final CouchbaseTemplate template;
public ExecutableRemoveByIdOperationSupport(final CouchbaseTemplate template) {
this.template = template;
}
@Override
@Deprecated
public ExecutableRemoveById removeById() {
return removeById(null);
}
@Override
public ExecutableRemoveById removeById(Class<?> domainType) {
return new ExecutableRemoveByIdSupport(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
null);
}
static class ExecutableRemoveByIdSupport implements ExecutableRemoveById {
private final CouchbaseTemplate template;
private final Class<?> domainType;
private final String scope;
private final String collection;
private final RemoveOptions options;
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Long cas;
private final ReactiveRemoveByIdSupport reactiveRemoveByIdSupport;
ExecutableRemoveByIdSupport(final CouchbaseTemplate template, final Class<?> domainType, final String scope,
final String collection, final RemoveOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
final DurabilityLevel durabilityLevel, Long cas) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.reactiveRemoveByIdSupport = new ReactiveRemoveByIdSupport(template.reactive(), domainType, scope, collection,
options, persistTo, replicateTo, durabilityLevel, cas);
this.cas = cas;
}
@Override
public RemoveResult one(final String id) {
return reactiveRemoveByIdSupport.one(id).block();
}
@Override
public RemoveResult oneEntity(final Object entity) {
return reactiveRemoveByIdSupport.oneEntity(entity).block();
}
@Override
public List<RemoveResult> all(final Collection<String> ids) {
return reactiveRemoveByIdSupport.all(ids).collectList().block();
}
@Override
public List<RemoveResult> allEntities(final Collection<Object> entities) {
return reactiveRemoveByIdSupport.allEntities(entities).collectList().block();
}
@Override
public RemoveByIdWithOptions inCollection(final String collection) {
return new ExecutableRemoveByIdSupport(template, domainType, scope,
collection != null ? collection : this.collection, options, persistTo, replicateTo, durabilityLevel, cas);
}
@Override
public RemoveByIdInScope withDurability(final DurabilityLevel durabilityLevel) {
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, cas);
}
@Override
public RemoveByIdInScope withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
Assert.notNull(persistTo, "PersistTo must not be null.");
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, cas);
}
@Override
public TerminatingRemoveById withOptions(final RemoveOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, cas);
}
@Override
public RemoveByIdInCollection inScope(final String scope) {
return new ExecutableRemoveByIdSupport(template, domainType, scope != null ? scope : this.scope, collection,
options, persistTo, replicateTo, durabilityLevel, cas);
}
@Override
public RemoveByIdWithDurability withCas(Long cas) {
return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, cas);
}
}
}

View File

@@ -1,136 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.List;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.WithConsistency;
import org.springframework.data.couchbase.core.support.WithQuery;
import org.springframework.data.couchbase.core.support.WithQueryOptions;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* RemoveBy Query Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ExecutableRemoveByQueryOperation {
/**
* Remove via the query service.
*/
<T> ExecutableRemoveByQuery<T> removeByQuery(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingRemoveByQuery<T> {
/**
* Remove all matching documents.
*
* @return RemoveResult for each matching document
*/
List<RemoveResult> all();
}
/**
* Fluent methods to specify the query
*
* @param <T> the entity type.
*/
interface RemoveByQueryWithQuery<T> extends TerminatingRemoveByQuery<T>, WithQuery<T> {
TerminatingRemoveByQuery<T> matching(Query query);
default TerminatingRemoveByQuery<T> matching(QueryCriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface RemoveByQueryWithOptions<T> extends RemoveByQueryWithQuery<T>, WithQueryOptions<RemoveResult> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
RemoveByQueryWithQuery<T> withOptions(QueryOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface RemoveByQueryInCollection<T> extends RemoveByQueryWithOptions<T>, InCollection<Object> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
RemoveByQueryWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface RemoveByQueryInScope<T> extends RemoveByQueryInCollection<T>, InScope<Object> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
RemoveByQueryInCollection<T> inScope(String scope);
}
@Deprecated
interface RemoveByQueryConsistentWith<T> extends RemoveByQueryInScope<T> {
@Deprecated
RemoveByQueryInScope<T> consistentWith(QueryScanConsistency scanConsistency);
}
interface RemoveByQueryWithConsistency<T> extends RemoveByQueryConsistentWith<T>, WithConsistency<T> {
@Override
RemoveByQueryConsistentWith<T> withConsistency(QueryScanConsistency scanConsistency);
}
/**
* Provides methods for constructing query operations in a fluent way.
*
* @param <T> the entity type.
*/
interface ExecutableRemoveByQuery<T> extends RemoveByQueryWithConsistency<T> {}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.List;
import org.springframework.data.couchbase.core.ReactiveRemoveByQueryOperationSupport.ReactiveRemoveByQuerySupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.util.Assert;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemoveByQueryOperation {
private static final Query ALL_QUERY = new Query();
private final CouchbaseTemplate template;
public ExecutableRemoveByQueryOperationSupport(final CouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ExecutableRemoveByQuery<T> removeByQuery(Class<T> domainType) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, ALL_QUERY, null,
OptionsBuilder.getScopeFrom(domainType), OptionsBuilder.getCollectionFrom(domainType), null);
}
static class ExecutableRemoveByQuerySupport<T> implements ExecutableRemoveByQuery<T> {
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final Query query;
private final ReactiveRemoveByQuerySupport<T> reactiveSupport;
private final QueryScanConsistency scanConsistency;
private final String scope;
private final String collection;
private final QueryOptions options;
ExecutableRemoveByQuerySupport(final CouchbaseTemplate template, final Class<T> domainType, final Query query,
final QueryScanConsistency scanConsistency, String scope, String collection, QueryOptions options) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.reactiveSupport = new ReactiveRemoveByQuerySupport<>(template.reactive(), domainType, query, scanConsistency,
scope, collection, options);
this.scanConsistency = scanConsistency;
this.scope = scope;
this.collection = collection;
this.options = options;
}
@Override
public List<RemoveResult> all() {
return reactiveSupport.all().collectList().block();
}
@Override
public TerminatingRemoveByQuery<T> matching(final Query query) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
options);
}
@Override
@Deprecated
public RemoveByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
options);
}
@Override
public RemoveByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
options);
}
@Override
public RemoveByQueryWithConsistency<T> inCollection(final String collection) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope,
collection != null ? collection : this.collection, options);
}
@Override
public RemoveByQueryWithQuery<T> withOptions(final QueryOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
options);
}
@Override
public RemoveByQueryInCollection<T> inScope(final String scope) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency,
scope != null ? scope : this.scope, collection, options);
}
}
}

View File

@@ -1,136 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllEntity;
import org.springframework.data.couchbase.core.support.WithDurability;
import org.springframework.data.couchbase.core.support.WithExpiry;
import org.springframework.data.couchbase.core.support.WithReplaceOptions;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplaceOptions;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* Replace Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ExecutableReplaceByIdOperation {
/**
* Replace using the KV service.
*
* @param domainType the entity type to replace.
*/
<T> ExecutableReplaceById<T> replaceById(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingReplaceById<T> extends OneAndAllEntity<T> {
/**
* Replace one entity.
*
* @return Replaced entity.
*/
@Override
T one(T object);
/**
* Replace a collection of entities.
*
* @return Replaced entities
*/
@Override
Collection<? extends T> all(Collection<? extends T> objects);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface ReplaceByIdWithOptions<T> extends TerminatingReplaceById<T>, WithReplaceOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
@Override
TerminatingReplaceById<T> withOptions(ReplaceOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface ReplaceByIdInCollection<T> extends ReplaceByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
ReplaceByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface ReplaceByIdInScope<T> extends ReplaceByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
ReplaceByIdInCollection<T> inScope(String scope);
}
interface ReplaceByIdWithDurability<T> extends ReplaceByIdInScope<T>, WithDurability<T> {
@Override
ReplaceByIdInScope<T> withDurability(DurabilityLevel durabilityLevel);
@Override
ReplaceByIdInScope<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
}
interface ReplaceByIdWithExpiry<T> extends ReplaceByIdWithDurability<T>, WithExpiry<T> {
@Override
ReplaceByIdWithDurability<T> withExpiry(final Duration expiry);
}
/**
* Provides methods for constructing KV replace operations in a fluent way.
*
* @param <T> the entity type to replace
*/
interface ExecutableReplaceById<T> extends ReplaceByIdWithExpiry<T> {}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.ReactiveReplaceByIdOperationSupport.ReactiveReplaceByIdSupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplaceOptions;
import com.couchbase.client.java.kv.ReplicateTo;
public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceByIdOperation {
private final CouchbaseTemplate template;
public ExecutableReplaceByIdOperationSupport(final CouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ExecutableReplaceById<T> replaceById(final Class<T> domainType) {
Assert.notNull(domainType, "DomainType must not be null!");
return new ExecutableReplaceByIdSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
null);
}
static class ExecutableReplaceByIdSupport<T> implements ExecutableReplaceById<T> {
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final String scope;
private final String collection;
private final ReplaceOptions options;
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveReplaceByIdSupport<T> reactiveSupport;
ExecutableReplaceByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String scope,
final String collection, ReplaceOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
final DurabilityLevel durabilityLevel, final Duration expiry) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.reactiveSupport = new ReactiveReplaceByIdSupport<>(template.reactive(), domainType, scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
}
@Override
public T one(final T object) {
return reactiveSupport.one(object).block();
}
@Override
public Collection<? extends T> all(Collection<? extends T> objects) {
return reactiveSupport.all(objects).collectList().block();
}
@Override
public ReplaceByIdWithOptions<T> inCollection(final String collection) {
return new ExecutableReplaceByIdSupport<>(template, domainType, scope,
collection != null ? collection : this.collection, options, persistTo, replicateTo, durabilityLevel, expiry);
}
@Override
public ReplaceByIdInScope<T> withDurability(final DurabilityLevel durabilityLevel) {
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
replicateTo, durabilityLevel, expiry);
}
@Override
public ReplaceByIdInScope<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
Assert.notNull(persistTo, "PersistTo must not be null.");
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
replicateTo, durabilityLevel, expiry);
}
@Override
public ReplaceByIdWithDurability<T> withExpiry(final Duration expiry) {
Assert.notNull(expiry, "expiry must not be null.");
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
replicateTo, durabilityLevel, expiry);
}
@Override
public TerminatingReplaceById<T> withOptions(final ReplaceOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
replicateTo, durabilityLevel, expiry);
}
@Override
public ReplaceByIdInCollection<T> inScope(final String scope) {
return new ExecutableReplaceByIdSupport<>(template, domainType, scope != null ? scope : this.scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry);
}
}
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllEntity;
import org.springframework.data.couchbase.core.support.WithDurability;
import org.springframework.data.couchbase.core.support.WithExpiry;
import org.springframework.data.couchbase.core.support.WithUpsertOptions;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
import com.couchbase.client.java.kv.UpsertOptions;
/**
* Insert Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ExecutableUpsertByIdOperation {
/**
* Upsert using the KV service.
*
* @param domainType the entity type to upsert.
*/
<T> ExecutableUpsertById<T> upsertById(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingUpsertById<T> extends OneAndAllEntity<T> {
/**
* Upsert one entity.
*
* @return Upserted entity.
*/
@Override
T one(T object);
/**
* Insert a collection of entities.
*
* @return Inserted entities
*/
@Override
Collection<? extends T> all(Collection<? extends T> objects);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use.
*/
interface UpsertByIdWithOptions<T> extends TerminatingUpsertById<T>, WithUpsertOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
@Override
TerminatingUpsertById<T> withOptions(UpsertOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface UpsertByIdInCollection<T> extends UpsertByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
UpsertByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface UpsertByIdInScope<T> extends UpsertByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
UpsertByIdInCollection<T> inScope(String scope);
}
interface UpsertByIdWithDurability<T> extends UpsertByIdInScope<T>, WithDurability<T> {
@Override
UpsertByIdInScope<T> withDurability(DurabilityLevel durabilityLevel);
@Override
UpsertByIdInScope<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
}
interface UpsertByIdWithExpiry<T> extends UpsertByIdWithDurability<T>, WithExpiry<T> {
@Override
UpsertByIdWithDurability<T> withExpiry(Duration expiry);
}
/**
* Provides methods for constructing KV operations in a fluent way.
*
* @param <T> the entity type to upsert
*/
interface ExecutableUpsertById<T> extends UpsertByIdWithExpiry<T> {}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.ReactiveUpsertByIdOperationSupport.ReactiveUpsertByIdSupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
import com.couchbase.client.java.kv.UpsertOptions;
public class ExecutableUpsertByIdOperationSupport implements ExecutableUpsertByIdOperation {
private final CouchbaseTemplate template;
public ExecutableUpsertByIdOperationSupport(final CouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ExecutableUpsertById<T> upsertById(final Class<T> domainType) {
Assert.notNull(domainType, "DomainType must not be null!");
return new ExecutableUpsertByIdSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
null);
}
static class ExecutableUpsertByIdSupport<T> implements ExecutableUpsertById<T> {
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final String scope;
private final String collection;
private final UpsertOptions options;
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveUpsertByIdSupport<T> reactiveSupport;
ExecutableUpsertByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String scope,
final String collection, final UpsertOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
final DurabilityLevel durabilityLevel, final Duration expiry) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.reactiveSupport = new ReactiveUpsertByIdSupport<>(template.reactive(), domainType, scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
}
@Override
public T one(final T object) {
return reactiveSupport.one(object).block();
}
@Override
public Collection<? extends T> all(Collection<? extends T> objects) {
return reactiveSupport.all(objects).collectList().block();
}
@Override
public TerminatingUpsertById<T> withOptions(final UpsertOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry);
}
@Override
public UpsertByIdInCollection<T> inScope(final String scope) {
return new ExecutableUpsertByIdSupport<>(template, domainType, scope != null ? scope : this.scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry);
}
@Override
public UpsertByIdWithOptions<T> inCollection(final String collection) {
return new ExecutableUpsertByIdSupport<>(template, domainType, scope,
collection != null ? collection : this.collection, options, persistTo, replicateTo, durabilityLevel, expiry);
}
@Override
public UpsertByIdInScope<T> withDurability(final DurabilityLevel durabilityLevel) {
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry);
}
@Override
public UpsertByIdInScope<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
Assert.notNull(persistTo, "PersistTo must not be null.");
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry);
}
@Override
public UpsertByIdWithDurability<T> withExpiry(final Duration expiry) {
Assert.notNull(expiry, "expiry must not be null.");
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry);
}
}
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
/**
* The fluent couchbase operations combines all different possible operations for simplicity reasons.
*/
public interface FluentCouchbaseOperations extends ExecutableUpsertByIdOperation, ExecutableInsertByIdOperation,
ExecutableReplaceByIdOperation, ExecutableFindByIdOperation, ExecutableFindFromReplicasByIdOperation,
ExecutableFindByQueryOperation, ExecutableFindByAnalyticsOperation, ExecutableExistsByIdOperation,
ExecutableRemoveByIdOperation, ExecutableRemoveByQueryOperation {}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2021-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Mono;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.transaction.CouchbaseResourceHolder;
/**
* Wrapper of {@link TemplateSupport} methods to adapt them to {@link ReactiveTemplateSupport}.
*
* @author Carlos Espinaco
* @author Michael Reiche
* @since 4.2
*/
public class NonReactiveSupportWrapper implements ReactiveTemplateSupport {
private final TemplateSupport support;
public NonReactiveSupportWrapper(TemplateSupport support) {
this.support = support;
}
@Override
public Mono<CouchbaseDocument> encodeEntity(Object entityToEncode) {
return Mono.fromSupplier(() -> support.encodeEntity(entityToEncode));
}
@Override
public <T> Mono<T> decodeEntity(String id, String source, Long cas, Class<T> entityClass, String scope, String collection,
Object txResultHolder, CouchbaseResourceHolder holder) {
return Mono.fromSupplier(() -> support.decodeEntity(id, source, cas, entityClass, scope, collection, txResultHolder, holder));
}
@Override
public <T> Mono<T> applyResult(T entity, CouchbaseDocument converted, Object id, Long cas,
Object txResultHolder, CouchbaseResourceHolder holder) {
return Mono.fromSupplier(() -> support.applyResult(entity, converted, id, cas, txResultHolder, holder));
}
@Override
public Long getCas(Object entity) {
return support.getCas(entity);
}
@Override
public Object getId(Object entity) {
return support.getId(entity);
}
@Override
public String getJavaNameForEntity(Class<?> clazz) {
return support.getJavaNameForEntity(clazz);
}
@Override
public TranslationService getTranslationService() {
return support.getTranslationService();
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.dao.TransientDataAccessException;
/**
* Data Access Exception that identifies Operations cancelled while being processed.
*
* @author Michael Nitschinger
*/
public class OperationCancellationException extends TransientDataAccessException {
/**
* Constructor for OperationCancellationException.
*
* @param msg the detail message
*/
public OperationCancellationException(final String msg) {
super(msg);
}
/**
* Constructor for OperationCancellationException.
*
* @param msg the detail message
* @param cause the root cause from the data access API in use
*/
public OperationCancellationException(final String msg, final Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.dao.TransientDataAccessException;
/**
* Data Access Exception that identifies Operations interrupted while being processed.
*
* @author Michael Nitschinger
*/
public class OperationInterruptedException extends TransientDataAccessException {
/**
* Constructor for OperationInterruptedException.
*
* @param msg the detail message
*/
public OperationInterruptedException(final String msg) {
super(msg);
}
/**
* Constructor for OperationInterruptedException.
*
* @param msg the detail message
* @param cause the root cause from the data access API in use
*/
public OperationInterruptedException(final String msg, final Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Mono;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.query.Query;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* Defines common operations on the Couchbase data source, most commonly implemented by
* {@link ReactiveCouchbaseTemplate}.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
public interface ReactiveCouchbaseOperations extends ReactiveFluentCouchbaseOperations {
/**
* Returns the converter used for this template/operations.
*/
CouchbaseConverter getConverter();
/**
* The name of the bucket used.
*/
String getBucketName();
/**
* The name of the scope used, null if the default scope is used.
*/
String getScopeName();
/**
* Returns the underlying client factory.
*/
CouchbaseClientFactory getCouchbaseClientFactory();
/**
* Save the entity to couchbase.<br>
* If there is no version property on the entity class, and this is in a transaction, use insert. <br>
* If there is no version property on the entity class, and this is not in a transaction, use upsert. <br>
* If there is a version property on the entity class, and it is non-zero, then this is an existing document, use
* replace.<br>
* Otherwise, there is a version property for the entity, but it is zero or null, use insert. <br>
*
* @param entity the entity to save in couchbase
* @param scopeAndCollection for use by repositories only. these are varargs for the scope and collection.
* @param <T> the entity class
* @return
*/
<T> Mono<T> save(T entity, String... scopeAndCollection);
/**
* Returns the count of documents found by the query.
* @param query
* @param domainType
* @param <T>
* @return
*/
<T> Mono<Long> count(Query query, Class<T> domainType);
/**
* @return the default consistency to use for queries
*/
QueryScanConsistency getConsistency();
}

View File

@@ -1,247 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Mono;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* template class for Reactive Couchbase operations
*
* @author Michael Nitschinger
* @author Michael Reiche
* @author Jorge Rodriguez Martin
* @author Carlos Espinaco
*/
public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, ApplicationContextAware {
private final CouchbaseClientFactory clientFactory;
private final CouchbaseConverter converter;
private final PersistenceExceptionTranslator exceptionTranslator;
private final ReactiveCouchbaseTemplateSupport templateSupport;
private ThreadLocal<PseudoArgs<?>> threadLocalArgs = new ThreadLocal<>();
private final QueryScanConsistency scanConsistency;
public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) {
this(clientFactory, converter, new JacksonTranslationService(), null);
}
public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
final TranslationService translationService) {
this(clientFactory, converter, translationService, null);
}
public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
final TranslationService translationService, final QueryScanConsistency scanConsistency) {
this.clientFactory = clientFactory;
this.converter = converter;
this.exceptionTranslator = clientFactory.getExceptionTranslator();
this.templateSupport = new ReactiveCouchbaseTemplateSupport(this, converter, translationService);
this.scanConsistency = scanConsistency;
}
@Override
public <T> Mono<T> save(T entity, String... scopeAndCollection) {
Assert.notNull(entity, "Entity must not be null!");
String scope = scopeAndCollection.length > 0 ? scopeAndCollection[0] : null;
String collection = scopeAndCollection.length > 1 ? scopeAndCollection[1] : null;
Mono<T> result;
final CouchbasePersistentEntity<?> mapperEntity = getConverter().getMappingContext()
.getPersistentEntity(entity.getClass());
final CouchbasePersistentProperty versionProperty = mapperEntity.getVersionProperty();
final boolean versionPresent = versionProperty != null;
final Long version = versionProperty == null || versionProperty.getField() == null ? null
: (Long) ReflectionUtils.getField(versionProperty.getField(), entity);
final boolean existingDocument = version != null && version > 0;
Class clazz = entity.getClass();
if (!versionPresent) { // the entity doesn't have a version property
// No version field - no cas
// If in a transaction, insert is the only thing that will work
if (TransactionalSupport.checkForTransactionInThreadLocalStorage().block().isPresent()) {
result = (Mono<T>) insertById(clazz).inScope(scope).inCollection(collection).one(entity);
} else { // if not in a tx, then upsert will work
result = (Mono<T>) upsertById(clazz).inScope(scope).inCollection(collection).one(entity);
}
} else if (existingDocument) { // there is a version property, and it is non-zero
// Updating existing document with cas
result = (Mono<T>) replaceById(clazz).inScope(scope).inCollection(collection).one(entity);
} else { // there is a version property, but it's zero or not set.
// Creating new document
result = (Mono<T>) insertById(clazz).inScope(scope).inCollection(collection).one(entity);
}
return result;
}
public <T> Mono<Long> count(Query query, Class<T> domainType) {
return findByQuery(domainType).matching(query).all().count();
}
@Override
public <T> ReactiveFindById<T> findById(Class<T> domainType) {
return new ReactiveFindByIdOperationSupport(this).findById(domainType);
}
@Override
public ReactiveExistsById existsById() {
return existsById(null);
}
@Override
public ReactiveExistsById existsById(Class<?> domainType) {
return new ReactiveExistsByIdOperationSupport(this).existsById(domainType);
}
@Override
public <T> ReactiveFindByAnalytics<T> findByAnalytics(Class<T> domainType) {
return new ReactiveFindByAnalyticsOperationSupport(this).findByAnalytics(domainType);
}
@Override
public <T> ReactiveFindByQuery<T> findByQuery(Class<T> domainType) {
return new ReactiveFindByQueryOperationSupport(this).findByQuery(domainType);
}
@Override
public <T> ReactiveFindFromReplicasById<T> findFromReplicasById(Class<T> domainType) {
return new ReactiveFindFromReplicasByIdOperationSupport(this).findFromReplicasById(domainType);
}
@Override
public <T> ReactiveInsertById<T> insertById(Class<T> domainType) {
return new ReactiveInsertByIdOperationSupport(this).insertById(domainType);
}
@Override
public ReactiveRemoveById removeById() {
return removeById(null);
}
@Override
public ReactiveRemoveById removeById(Class<?> domainType) {
return new ReactiveRemoveByIdOperationSupport(this).removeById(domainType);
}
@Override
public <T> ReactiveRemoveByQuery<T> removeByQuery(Class<T> domainType) {
return new ReactiveRemoveByQueryOperationSupport(this).removeByQuery(domainType);
}
@Override
public <T> ReactiveReplaceById<T> replaceById(Class<T> domainType) {
return new ReactiveReplaceByIdOperationSupport(this).replaceById(domainType);
}
@Override
public <T> ReactiveUpsertById<T> upsertById(Class<T> domainType) {
return new ReactiveUpsertByIdOperationSupport(this).upsertById(domainType);
}
@Override
public String getBucketName() {
return clientFactory.getBucket().name();
}
@Override
public String getScopeName() {
return clientFactory.getScope().name();
}
@Override
public CouchbaseClientFactory getCouchbaseClientFactory() {
return clientFactory;
}
/**
* Provides access to a {@link Collection} on the configured {@link CouchbaseClientFactory}.
*
* @param collectionName the name of the collection, if null is passed in the default collection is assumed.
* @return the collection instance.
*/
public Collection getCollection(final String collectionName) {
return clientFactory.getCollection(collectionName);
}
@Override
public CouchbaseConverter getConverter() {
return converter;
}
public ReactiveTemplateSupport support() {
return templateSupport;
}
/**
* Tries to convert the given {@link RuntimeException} into a {@link DataAccessException} but returns the original
* exception if the conversation failed. Thus allows safe re-throwing of the return value.
*
* @param ex the exception to translate
*/
RuntimeException potentiallyConvertRuntimeException(final RuntimeException ex) {
RuntimeException resolved = exceptionTranslator != null ? exceptionTranslator.translateExceptionIfPossible(ex)
: null;
return resolved == null ? ex : resolved;
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
templateSupport.setApplicationContext(applicationContext);
}
/**
* @return the pseudoArgs from the ThreadLocal field
*/
public PseudoArgs<?> getPseudoArgs() {
return threadLocalArgs == null ? null : threadLocalArgs.get();
}
/**
* set the ThreadLocal field
*/
public void setPseudoArgs(PseudoArgs<?> threadLocalArgs) {
this.threadLocalArgs = new ThreadLocal<>();
this.threadLocalArgs.set(threadLocalArgs);
}
/**
* {@inheritDoc}
*/
@Override
public QueryScanConsistency getConsistency() {
return scanConsistency;
}
}

View File

@@ -1,125 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Mono;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.event.BeforeConvertEvent;
import org.springframework.data.couchbase.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.couchbase.core.mapping.event.ReactiveAfterConvertCallback;
import org.springframework.data.couchbase.core.mapping.event.ReactiveBeforeConvertCallback;
import org.springframework.data.couchbase.transaction.CouchbaseResourceHolder;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
import org.springframework.util.Assert;
/**
* Internal encode/decode support for {@link ReactiveCouchbaseTemplate}.
*
* @author Carlos Espinaco
* @author Michael Reiche
* @since 4.2
*/
class ReactiveCouchbaseTemplateSupport extends AbstractTemplateSupport
implements ApplicationContextAware, ReactiveTemplateSupport {
private final ReactiveCouchbaseTemplate template;
private ReactiveEntityCallbacks reactiveEntityCallbacks;
public ReactiveCouchbaseTemplateSupport(final ReactiveCouchbaseTemplate template, final CouchbaseConverter converter,
final TranslationService translationService) {
super(template, converter, translationService);
this.template = template;
}
@Override
public Mono<CouchbaseDocument> encodeEntity(final Object entityToEncode) {
return Mono.just(entityToEncode).doOnNext(entity -> maybeEmitEvent(new BeforeConvertEvent<>(entity)))
.flatMap(entity -> maybeCallBeforeConvert(entity, "")).map(maybeNewEntity -> {
final CouchbaseDocument converted = new CouchbaseDocument();
converter.write(maybeNewEntity, converted);
return converted;
}).flatMap(converted -> maybeCallAfterConvert(entityToEncode, converted, "").thenReturn(converted))
.doOnNext(converted -> maybeEmitEvent(new BeforeSaveEvent<>(entityToEncode, converted)));
}
@Override
ReactiveCouchbaseTemplate getReactiveTemplate() {
return template;
}
@Override
public <T> Mono<T> decodeEntity(String id, String source, Long cas, Class<T> entityClass, String scope,
String collection, Object txResultHolder, CouchbaseResourceHolder holder) {
return Mono
.fromSupplier(() -> decodeEntityBase(id, source, cas, entityClass, scope, collection, txResultHolder, holder));
}
@Override
public <T> Mono<T> applyResult(T entity, CouchbaseDocument converted, Object id, Long cas,
Object txResultHolder, CouchbaseResourceHolder holder) {
return Mono.fromSupplier(() -> applyResultBase(entity, converted, id, cas, txResultHolder, holder));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
if (reactiveEntityCallbacks == null) {
setReactiveEntityCallbacks(ReactiveEntityCallbacks.create(applicationContext));
}
}
/**
* Set the {@link ReactiveEntityCallbacks} instance to use when invoking
* {@link org.springframework.data.mapping.callback.ReactiveEntityCallbacks callbacks} like the
* {@link ReactiveBeforeConvertCallback}.
* <p>
* Overrides potentially existing {@link EntityCallbacks}.
*
* @param reactiveEntityCallbacks must not be {@literal null}.
* @throws IllegalArgumentException if the given instance is {@literal null}.
*/
public void setReactiveEntityCallbacks(ReactiveEntityCallbacks reactiveEntityCallbacks) {
Assert.notNull(reactiveEntityCallbacks, "EntityCallbacks must not be null!");
this.reactiveEntityCallbacks = reactiveEntityCallbacks;
}
protected <T> Mono<T> maybeCallBeforeConvert(T object, String collection) {
if (reactiveEntityCallbacks != null) {
return reactiveEntityCallbacks.callback(ReactiveBeforeConvertCallback.class, object, collection);
} else {
LOG.info("maybeCallBeforeConvert called, but ReactiveCouchbaseTemplate not initialized with applicationContext");
}
return Mono.just(object);
}
protected <T> Mono<T> maybeCallAfterConvert(T object, CouchbaseDocument document, String collection) {
if (null != reactiveEntityCallbacks) {
return reactiveEntityCallbacks.callback(ReactiveAfterConvertCallback.class, object, document, collection);
} else {
LOG.info("maybeCallAfterConvert called, but ReactiveCouchbaseTemplate not initialized with applicationContext");
}
return Mono.just(object);
}
}

View File

@@ -1,118 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Mono;
import java.util.Collection;
import java.util.Map;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllExistsReactive;
import org.springframework.data.couchbase.core.support.WithExistsOptions;
import com.couchbase.client.java.kv.ExistsOptions;
/**
* Exists Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ReactiveExistsByIdOperation {
/**
* Checks if the document exists in the bucket.
*/
@Deprecated
ReactiveExistsById existsById();
/**
* Checks if the document exists in the bucket.
*/
ReactiveExistsById existsById(Class<?> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingExistsById extends OneAndAllExistsReactive {
/**
* Performs the operation on the ID given.
*
* @param id the ID to perform the operation on.
* @return true if the document exists, false otherwise.
*/
@Override
Mono<Boolean> one(String id);
/**
* Performs the operation on the collection of ids.
*
* @param ids the ids to check.
* @return a map consisting of the document IDs as the keys and if they exist as the value.
*/
@Override
Mono<Map<String, Boolean>> all(Collection<String> ids);
}
/**
* Fluent method to specify options.
*/
interface ExistsByIdWithOptions extends TerminatingExistsById, WithExistsOptions {
/**
* Fluent method to specify options to use for execution.
*
* @param options to use for execution
*/
@Override
TerminatingExistsById withOptions(ExistsOptions options);
}
/**
* Fluent method to specify the collection.
*/
interface ExistsByIdInCollection extends ExistsByIdWithOptions, InCollection {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
ExistsByIdWithOptions inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*/
interface ExistsByIdInScope extends ExistsByIdInCollection, InScope {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
ExistsByIdInCollection inScope(String scope);
}
/**
* Provides methods for constructing KV exists operations in a fluent way.
*/
interface ReactiveExistsById extends ExistsByIdInScope {}
}

View File

@@ -1,127 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
import java.util.Collection;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.util.Assert;
import com.couchbase.client.java.kv.ExistsOptions;
import com.couchbase.client.java.kv.ExistsResult;
/**
* ReactiveExistsById Support
*
* @author Michael Reiche
*/
public class ReactiveExistsByIdOperationSupport implements ReactiveExistsByIdOperation {
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveExistsByIdOperationSupport.class);
ReactiveExistsByIdOperationSupport(ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
@Deprecated
public ReactiveExistsById existsById() {
return existsById(null);
}
@Override
public ReactiveExistsById existsById(Class<?> domainType) {
return new ReactiveExistsByIdSupport(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null);
}
static class ReactiveExistsByIdSupport implements ReactiveExistsById {
private final ReactiveCouchbaseTemplate template;
private final Class<?> domainType;
private final String scope;
private final String collection;
private final ExistsOptions options;
ReactiveExistsByIdSupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType, final String scope,
final String collection, final ExistsOptions options) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
}
@Override
public Mono<Boolean> one(final String id) {
PseudoArgs<ExistsOptions> pArgs = new PseudoArgs<>(template, scope, collection, options, domainType);
if (LOG.isDebugEnabled()) {
LOG.debug("existsById key={} {}", id, pArgs);
}
return TransactionalSupport.verifyNotInTransaction("existsById").then(Mono.just(id))
.flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
.getCollection(pArgs.getCollection()).reactive().exists(id, buildOptions(pArgs.getOptions()))
.map(ExistsResult::exists))
.onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
});
}
private ExistsOptions buildOptions(ExistsOptions options) {
return OptionsBuilder.buildExistsOptions(options);
}
@Override
public Mono<Map<String, Boolean>> all(final Collection<String> ids) {
return Flux.fromIterable(ids).flatMap(id -> one(id).map(result -> Tuples.of(id, result)))
.collectMap(Tuple2::getT1, Tuple2::getT2);
}
@Override
public ExistsByIdWithOptions inCollection(final String collection) {
return new ReactiveExistsByIdSupport(template, domainType, scope,
collection != null ? collection : this.collection, options);
}
@Override
public TerminatingExistsById withOptions(final ExistsOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveExistsByIdSupport(template, domainType, scope, collection, options);
}
@Override
public ExistsByIdInCollection inScope(final String scope) {
return new ReactiveExistsByIdSupport(template, domainType, scope != null ? scope : this.scope, collection,
options);
}
}
}

View File

@@ -1,191 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllReactive;
import org.springframework.data.couchbase.core.support.WithAnalyticsConsistency;
import org.springframework.data.couchbase.core.support.WithAnalyticsOptions;
import org.springframework.data.couchbase.core.support.WithAnalyticsQuery;
import com.couchbase.client.java.analytics.AnalyticsOptions;
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
/**
* FindByAnalytics Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ReactiveFindByAnalyticsOperation {
/**
* Queries the analytics service.
*
* @param domainType the entity type to use for the results.
*/
<T> ReactiveFindByAnalytics<T> findByAnalytics(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingFindByAnalytics<T> extends OneAndAllReactive {
/**
* Get exactly zero or one result.
*
* @return a mono with the match if found (an empty one otherwise).
* @throws IncorrectResultSizeDataAccessException if more than one match found.
*/
Mono<T> one();
/**
* Get the first or no result.
*
* @return the first or an empty mono if none found.
*/
Mono<T> first();
/**
* Get all matching elements.
*
* @return never {@literal null}.
*/
Flux<T> all();
/**
* Get the number of matching elements.
*
* @return total number of matching elements.
*/
Mono<Long> count();
/**
* Check for the presence of matching elements.
*
* @return {@literal true} if at least one matching element exists.
*/
Mono<Boolean> exists();
}
interface FindByAnalyticsWithQuery<T> extends TerminatingFindByAnalytics<T>, WithAnalyticsQuery<T> {
/**
* Set the filter for the analytics query to be used.
*
* @param query must not be {@literal null}.
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingFindByAnalytics<T> matching(AnalyticsQuery query);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use.
*/
interface FindByAnalyticsWithOptions<T> extends FindByAnalyticsWithQuery<T>, WithAnalyticsOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
@Override
TerminatingFindByAnalytics<T> withOptions(AnalyticsOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface FindByAnalyticsInCollection<T> extends FindByAnalyticsWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
FindByAnalyticsWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface FindByAnalyticsInScope<T> extends FindByAnalyticsInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
FindByAnalyticsInCollection<T> inScope(String scope);
}
@Deprecated
interface FindByAnalyticsConsistentWith<T> extends FindByAnalyticsInScope<T> {
/**
* Allows to override the default scan consistency.
*
* @param scanConsistency the custom scan consistency to use for this analytics query.
*/
@Deprecated
FindByAnalyticsWithQuery<T> consistentWith(AnalyticsScanConsistency scanConsistency);
}
interface FindByAnalyticsWithConsistency<T> extends FindByAnalyticsInScope<T>, WithAnalyticsConsistency<T> {
/**
* Allows to override the default scan consistency.
*
* @param scanConsistency the custom scan consistency to use for this analytics query.
*/
@Override
FindByAnalyticsWithQuery<T> withConsistency(AnalyticsScanConsistency scanConsistency);
}
/**
* Result type override (Optional).
*/
interface FindByAnalyticsWithProjection<T> extends FindByAnalyticsWithConsistency<T> {
/**
* Define the target type fields should be mapped to. <br />
* Skip this step if you are anyway only interested in the original domain type.
*
* @param returnType must not be {@literal null}.
* @return new instance of {@link FindByAnalyticsWithConsistency}.
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
<R> FindByAnalyticsWithConsistency<R> as(Class<R> returnType);
}
interface ReactiveFindByAnalytics<T> extends FindByAnalyticsWithProjection<T>, FindByAnalyticsConsistentWith<T> {}
}

View File

@@ -1,217 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.support.TemplateUtils;
import org.springframework.util.Assert;
import com.couchbase.client.java.analytics.AnalyticsOptions;
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
import com.couchbase.client.java.analytics.ReactiveAnalyticsResult;
public class ReactiveFindByAnalyticsOperationSupport implements ReactiveFindByAnalyticsOperation {
private static final AnalyticsQuery ALL_QUERY = new AnalyticsQuery();
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByAnalyticsOperationSupport.class);
public ReactiveFindByAnalyticsOperationSupport(final ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ReactiveFindByAnalytics<T> findByAnalytics(final Class<T> domainType) {
return new ReactiveFindByAnalyticsSupport<>(template, domainType, domainType, ALL_QUERY, null,
OptionsBuilder.getScopeFrom(domainType), OptionsBuilder.getCollectionFrom(domainType), null,
template.support());
}
static class ReactiveFindByAnalyticsSupport<T> implements ReactiveFindByAnalytics<T> {
private final ReactiveCouchbaseTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final AnalyticsQuery query;
private final AnalyticsScanConsistency scanConsistency;
private final String scope;
private final String collection;
private final AnalyticsOptions options;
private final ReactiveTemplateSupport support;
ReactiveFindByAnalyticsSupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType,
final Class<T> returnType, final AnalyticsQuery query, final AnalyticsScanConsistency scanConsistency,
String scope, String collection, AnalyticsOptions options, ReactiveTemplateSupport support) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.query = query;
this.scanConsistency = scanConsistency;
this.scope = scope;
this.collection = collection;
this.options = options;
this.support = support;
}
@Override
public TerminatingFindByAnalytics<T> matching(AnalyticsQuery query) {
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, support);
}
@Override
@Deprecated
public FindByAnalyticsWithQuery<T> consistentWith(AnalyticsScanConsistency scanConsistency) {
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, support);
}
@Override
public FindByAnalyticsWithQuery<T> withConsistency(AnalyticsScanConsistency scanConsistency) {
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, support);
}
@Override
public <R> FindByAnalyticsWithConsistency<R> as(final Class<R> returnType) {
Assert.notNull(returnType, "returnType must not be null!");
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, support);
}
@Override
public Mono<T> one() {
return all().singleOrEmpty();
}
@Override
public Mono<T> first() {
return all().next();
}
@Override
public Flux<T> all() {
return Flux.defer(() -> {
String statement = assembleEntityQuery(false);
if (LOG.isDebugEnabled()) {
LOG.debug("findByAnalytics statement: {}", statement);
}
return TransactionalSupport.verifyNotInTransaction("findByAnalytics").then(template.getCouchbaseClientFactory()
.getCluster().reactive().analyticsQuery(statement, buildAnalyticsOptions())).onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
}).flatMapMany(ReactiveAnalyticsResult::rowsAsObject).flatMap(row -> {
String id = null;
Long cas = null;
id = row.getString(TemplateUtils.SELECT_ID);
if (id == null) {
id = row.getString(TemplateUtils.SELECT_ID_3x);
row.removeKey(TemplateUtils.SELECT_ID_3x);
}
cas = row.getLong(TemplateUtils.SELECT_CAS);
if (cas == null) {
cas = row.getLong(TemplateUtils.SELECT_CAS_3x);
row.removeKey(TemplateUtils.SELECT_CAS_3x);
}
row.removeKey(TemplateUtils.SELECT_ID);
row.removeKey(TemplateUtils.SELECT_CAS);
return support.decodeEntity(id, row.toString(), cas, returnType, null, null, null, null);
});
});
}
@Override
public Mono<Long> count() {
return Mono.defer(() -> {
String statement = assembleEntityQuery(true);
if (LOG.isDebugEnabled()) {
LOG.debug("findByAnalytics statement: {}", statement);
}
return TransactionalSupport.verifyNotInTransaction("findByAnalytics").then(template.getCouchbaseClientFactory()
.getCluster().reactive().analyticsQuery(statement, buildAnalyticsOptions())).onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
}).flatMapMany(ReactiveAnalyticsResult::rowsAsObject)
.map(row -> row.getLong(row.getNames().iterator().next())).next();
});
}
@Override
public Mono<Boolean> exists() {
return count().map(count -> count > 0);
}
@Override
public TerminatingFindByAnalytics<T> withOptions(final AnalyticsOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, support);
}
@Override
public FindByAnalyticsInCollection<T> inScope(final String scope) {
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency,
scope != null ? scope : this.scope, collection, options, support);
}
@Override
public FindByAnalyticsWithConsistency<T> inCollection(final String collection) {
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection != null ? collection : this.collection, options, support);
}
private String assembleEntityQuery(final boolean count) {
final String bucket = "`" + template.getBucketName() + "`";
final StringBuilder statement = new StringBuilder("SELECT ");
if (count) {
statement.append("count(*) as __count");
} else {
statement.append("meta().id as __id, meta().cas as __cas, ").append(bucket).append(".*");
}
final String dataset = support.getJavaNameForEntity(domainType);
statement.append(" FROM ").append(dataset);
query.appendSort(statement);
query.appendSkipAndLimit(statement);
return statement.toString();
}
private AnalyticsOptions buildAnalyticsOptions() {
final AnalyticsOptions options = AnalyticsOptions.analyticsOptions();
if (scanConsistency != null) {
options.scanConsistency(scanConsistency);
}
return options;
}
}
}

View File

@@ -1,146 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllIdReactive;
import org.springframework.data.couchbase.core.support.WithExpiry;
import org.springframework.data.couchbase.core.support.WithGetOptions;
import org.springframework.data.couchbase.core.support.WithProjectionId;
import com.couchbase.client.java.kv.GetOptions;
/**
* Get Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ReactiveFindByIdOperation {
/**
* Loads a document from a bucket.
*
* @param domainType the entity type to use for the results.
*/
<T> ReactiveFindById<T> findById(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*
* @param <T> the entity type to use for the results.
*/
interface TerminatingFindById<T> extends OneAndAllIdReactive<T> {
/**
* Finds one document based on the given ID.
*
* @param id the document ID.
* @return the entity if found.
*/
Mono<T> one(String id);
/**
* Finds a list of documents based on the given IDs.
*
* @param ids the document ID ids.
* @return the list of found entities.
*/
Flux<? extends T> all(Collection<String> ids);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface FindByIdWithOptions<T> extends TerminatingFindById<T>, WithGetOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options options to use for execution
*/
@Override
TerminatingFindById<T> withOptions(GetOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface FindByIdInCollection<T> extends FindByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
FindByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface FindByIdInScope<T> extends FindByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
FindByIdInCollection<T> inScope(String scope);
}
interface FindByIdWithProjection<T> extends FindByIdInScope<T>, WithProjectionId<T> {
/**
* Load only certain fields for the document.
*
* @param fields the projected fields to load.
*/
FindByIdInCollection<T> project(String... fields);
}
interface FindByIdWithExpiry<T> extends FindByIdWithProjection<T>, WithExpiry<T> {
/**
* Load only certain fields for the document.
*
* @param expiry the projected fields to load.
*/
@Override
FindByIdWithProjection<T> withExpiry(Duration expiry);
}
/**
* Provides methods for constructing query operations in a fluent way.
*
* @param <T> the entity type to use for the results
*/
interface ReactiveFindById<T> extends FindByIdWithExpiry<T> {}
}

View File

@@ -1,218 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import static com.couchbase.client.java.kv.GetAndTouchOptions.getAndTouchOptions;
import static com.couchbase.client.java.transactions.internal.ConverterUtil.makeCollectionIdentifier;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.util.Assert;
import com.couchbase.client.core.error.DocumentNotFoundException;
import com.couchbase.client.java.CommonOptions;
import com.couchbase.client.java.ReactiveCollection;
import com.couchbase.client.java.codec.RawJsonTranscoder;
import com.couchbase.client.java.kv.GetAndTouchOptions;
import com.couchbase.client.java.kv.GetOptions;
/**
* {@link ReactiveFindByIdOperation} implementations for Couchbase.
*
* @author Michael Reiche
*/
public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperation {
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByIdOperationSupport.class);
ReactiveFindByIdOperationSupport(ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ReactiveFindById<T> findById(Class<T> domainType) {
return new ReactiveFindByIdSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, null, null, template.support());
}
static class ReactiveFindByIdSupport<T> implements ReactiveFindById<T> {
private final ReactiveCouchbaseTemplate template;
private final Class<T> domainType;
private final String scope;
private final String collection;
private final CommonOptions<?> options;
private final List<String> fields;
private final ReactiveTemplateSupport support;
private final Duration expiry;
ReactiveFindByIdSupport(ReactiveCouchbaseTemplate template, Class<T> domainType, String scope, String collection,
CommonOptions<?> options, List<String> fields, Duration expiry, ReactiveTemplateSupport support) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.fields = fields;
this.expiry = expiry;
this.support = support;
}
@Override
public Mono<T> one(final String id) {
CommonOptions<?> gOptions = initGetOptions();
PseudoArgs<?> pArgs = new PseudoArgs(template, scope, collection, gOptions, domainType);
if (LOG.isDebugEnabled()) {
LOG.debug("findById key={} {}", id, pArgs);
}
ReactiveCollection rc = template.getCouchbaseClientFactory().withScope(pArgs.getScope())
.getCollection(pArgs.getCollection()).reactive();
Mono<T> reactiveEntity = TransactionalSupport.checkForTransactionInThreadLocalStorage().flatMap(ctxOpt -> {
if (!ctxOpt.isPresent()) {
if (pArgs.getOptions() instanceof GetAndTouchOptions) {
return rc.getAndTouch(id, expiryToUse(), (GetAndTouchOptions) pArgs.getOptions())
.flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType,
pArgs.getScope(), pArgs.getCollection(), null, null));
} else {
return rc.get(id, (GetOptions) pArgs.getOptions())
.flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType,
pArgs.getScope(), pArgs.getCollection(), null, null));
}
} else {
rejectInvalidTransactionalOptions();
return ctxOpt.get().getCore().get(makeCollectionIdentifier(rc.async()), id)
.flatMap(result -> support.decodeEntity(id, new String(result.contentAsBytes(), StandardCharsets.UTF_8),
result.cas(), domainType, pArgs.getScope(), pArgs.getCollection(),
null, null));
}
});
return reactiveEntity.onErrorResume(throwable -> {
if (throwable instanceof DocumentNotFoundException) {
return Mono.empty();
}
return Mono.error(throwable);
}).onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
});
}
private void rejectInvalidTransactionalOptions() {
if (this.expiry != null) {
throw new IllegalArgumentException("withExpiry is not supported in a transaction");
}
if (this.options != null) {
throw new IllegalArgumentException("withOptions is not supported in a transaction");
}
if (this.fields != null) {
throw new IllegalArgumentException("project is not supported in a transaction");
}
}
@Override
public Flux<? extends T> all(final Collection<String> ids) {
return Flux.fromIterable(ids).flatMap(this::one);
}
@Override
public FindByIdInScope<T> withOptions(final GetOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, fields, expiry, support);
}
@Override
public FindByIdWithOptions<T> inCollection(final String collection) {
return new ReactiveFindByIdSupport<>(template, domainType, scope,
collection != null ? collection : this.collection, options, fields, expiry, support);
}
@Override
public FindByIdInCollection<T> inScope(final String scope) {
return new ReactiveFindByIdSupport<>(template, domainType, scope != null ? scope : this.scope, collection,
options, fields, expiry, support);
}
@Override
public FindByIdInCollection<T> project(String... fields) {
Assert.notNull(fields, "Fields must not be null");
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields),
expiry, support);
}
@Override
public FindByIdWithProjection<T> withExpiry(final Duration expiry) {
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, fields, expiry, support);
}
private CommonOptions<?> initGetOptions() {
CommonOptions<?> getOptions;
if (expiry != null || options instanceof GetAndTouchOptions) {
GetAndTouchOptions gOptions = options != null ? (GetAndTouchOptions) options : getAndTouchOptions();
if (gOptions.build().transcoder() == null) {
gOptions.transcoder(RawJsonTranscoder.INSTANCE);
}
getOptions = gOptions;
} else {
GetOptions gOptions = options != null ? (GetOptions) options : GetOptions.getOptions();
if (gOptions.build().transcoder() == null) {
gOptions.transcoder(RawJsonTranscoder.INSTANCE);
}
if (fields != null && !fields.isEmpty()) {
gOptions.project(fields);
}
getOptions = gOptions;
}
return getOptions;
}
private Duration expiryToUse() {
Duration expiryToUse = expiry;
if (expiryToUse != null || options instanceof GetAndTouchOptions) {
if (expiryToUse == null) { // GetAndTouchOptions without specifying expiry -> get expiry from annoation
final CouchbasePersistentEntity<?> entity = template.getConverter().getMappingContext()
.getRequiredPersistentEntity(domainType);
expiryToUse = entity.getExpiryDuration();
}
}
return expiryToUse;
}
}
}

View File

@@ -1,246 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllReactive;
import org.springframework.data.couchbase.core.support.WithConsistency;
import org.springframework.data.couchbase.core.support.WithDistinct;
import org.springframework.data.couchbase.core.support.WithQuery;
import org.springframework.data.couchbase.core.support.WithQueryOptions;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* ReactiveFindByQueryOperation<br>
* Queries the N1QL service.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
public interface ReactiveFindByQueryOperation {
/**
* Create the operation for the domainType
*
* @param domainType the entity type to use for the results.
*/
<T> ReactiveFindByQuery<T> findByQuery(Class<T> domainType);
/**
* Compose find execution by calling one of the terminating methods.
*/
interface TerminatingFindByQuery<T> extends OneAndAllReactive<T> {
/**
* Get exactly zero or one result.
*
* @return a mono with the match if found (an empty one otherwise).
* @throws IncorrectResultSizeDataAccessException if more than one match found.
*/
Mono<T> one();
/**
* Get the first or no result.
*
* @return the first or an empty mono if none found.
*/
Mono<T> first();
/**
* Get all matching elements.
*
* @return never {@literal null}.
*/
Flux<T> all();
/**
* Get the number of matching elements.
*
* @return total number of matching elements.
*/
Mono<Long> count();
/**
* Check for the presence of matching elements.
*
* @return {@literal true} if at least one matching element exists.
*/
Mono<Boolean> exists();
}
/**
* Fluent methods to filter by query
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithQuery<T> extends TerminatingFindByQuery<T>, WithQuery<T> {
/**
* Set the filter {@link Query} to be used.
*
* @param query must not be {@literal null}.
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingFindByQuery<T> matching(Query query);
/**
* Set the filter {@link QueryCriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingFindByQuery}.
* @throws IllegalArgumentException if criteria is {@literal null}.
*/
default TerminatingFindByQuery<T> matching(QueryCriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithOptions<T> extends FindByQueryWithQuery<T>, WithQueryOptions<T> {
/**
* @param options options to use for execution
*/
TerminatingFindByQuery<T> withOptions(QueryOptions options);
}
/**
* Fluent method to specify the collection
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryInCollection<T> extends FindByQueryWithOptions<T>, InCollection<T> {
FindByQueryWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryInScope<T> extends FindByQueryInCollection<T>, InScope<T> {
FindByQueryInCollection<T> inScope(String scope);
}
/**
* To be removed at the next major release. use WithConsistency instead
*
* @param <T> the entity type to use for the results.
*/
@Deprecated
interface FindByQueryConsistentWith<T> extends FindByQueryInScope<T> {
/**
* Allows to override the default scan consistency.
*
* @param scanConsistency the custom scan consistency to use for this query.
*/
@Deprecated
FindByQueryInScope<T> consistentWith(QueryScanConsistency scanConsistency);
}
/**
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithConsistency<T> extends FindByQueryConsistentWith<T>, WithConsistency<T> {
/**
* Allows to override the default scan consistency.
*
* @param scanConsistency the custom scan consistency to use for this query.
*/
FindByQueryConsistentWith<T> withConsistency(QueryScanConsistency scanConsistency);
}
/**
* Fluent method to specify a return type different than the the entity type to use for the results.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithProjection<T> extends FindByQueryWithConsistency<T> {
/**
* Define the target type fields should be mapped to. <br />
* Skip this step if you are anyway only interested in the original domain type.
*
* @param returnType must not be {@literal null}.
* @return new instance of {@link FindByQueryWithProjection}.
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
<R> FindByQueryWithConsistency<R> as(Class<R> returnType);
}
/**
* Fluent method to specify fields to project.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {
/**
* Define the target type fields should be mapped to. <br />
* Skip this step if you are anyway only interested in the original domain type.
*
* @param fields to project
* @return new instance of {@link FindByQueryWithConsistency}.
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
FindByQueryWithProjection<T> project(String[] fields);
}
/**
* Fluent method to specify DISTINCT fields
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithDistinct<T> {
/**
* Finds the distinct values for a specified {@literal field} across a single {@link } or view.
*
* @param distinctFields name of the field. Must not be {@literal null}.
* @return new instance of {@link ReactiveFindByQuery}.
* @throws IllegalArgumentException if field is {@literal null}.
*/
FindByQueryWithProjection<T> distinct(String[] distinctFields);
}
/**
* provides methods for constructing query operations in a fluent way.
*
* @param <T> the entity type to use for the results
*/
interface ReactiveFindByQuery<T> extends FindByQueryWithDistinct<T> {}
}

View File

@@ -1,282 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.data.couchbase.core.support.TemplateUtils;
import org.springframework.util.Assert;
import com.couchbase.client.java.ReactiveScope;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
import com.couchbase.client.java.query.ReactiveQueryResult;
import com.couchbase.client.java.transactions.AttemptContextReactiveAccessor;
import com.couchbase.client.java.transactions.TransactionQueryOptions;
import com.couchbase.client.java.transactions.TransactionQueryResult;
/**
* {@link ReactiveFindByQueryOperation} implementations for Couchbase.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByQueryOperationSupport.class);
public ReactiveFindByQueryOperationSupport(final ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ReactiveFindByQuery<T> findByQuery(final Class<T> domainType) {
return new ReactiveFindByQuerySupport<>(template, domainType, domainType, ALL_QUERY, null,
OptionsBuilder.getScopeFrom(domainType), OptionsBuilder.getCollectionFrom(domainType), null, null, null,
template.support());
}
static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
private final ReactiveCouchbaseTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final Query query;
private final QueryScanConsistency scanConsistency;
private final String collection;
private final String scope;
private final String[] distinctFields;
private final String[] fields;
private final QueryOptions options;
private final ReactiveTemplateSupport support;
ReactiveFindByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType,
final Class<T> returnType, final Query query, final QueryScanConsistency scanConsistency, final String scope,
final String collection, final QueryOptions options, final String[] distinctFields, final String[] fields,
final ReactiveTemplateSupport support) {
Assert.notNull(domainType, "domainType must not be null!");
Assert.notNull(returnType, "returnType must not be null!");
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.query = query;
this.scanConsistency = scanConsistency;
this.scope = scope;
this.collection = collection;
this.options = options;
this.distinctFields = distinctFields;
this.fields = fields;
this.support = support;
}
@Override
public FindByQueryWithQuery<T> matching(Query query) {
QueryScanConsistency scanCons;
if (query.getScanConsistency() != null) { // redundant, since buildQueryOptions() will use
// query.getScanConsistency()
scanCons = query.getScanConsistency();
} else {
scanCons = scanConsistency;
}
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
options, distinctFields, fields, support);
}
@Override
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields, support);
}
@Override
public FindByQueryInCollection<T> inScope(final String scope) {
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency,
scope != null ? scope : this.scope, collection, options, distinctFields, fields, support);
}
@Override
public FindByQueryWithConsistency<T> inCollection(final String collection) {
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection != null ? collection : this.collection, options, distinctFields, fields, support);
}
@Override
@Deprecated
public FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency) {
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields, support);
}
@Override
public FindByQueryWithConsistency<T> withConsistency(QueryScanConsistency scanConsistency) {
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields, support);
}
public <R> FindByQueryWithConsistency<R> as(Class<R> returnType) {
Assert.notNull(returnType, "returnType must not be null!");
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields, support);
}
@Override
public FindByQueryWithProjection<T> project(String[] fields) {
Assert.notNull(fields, "Fields must not be null");
Assert.isNull(distinctFields, "only one of project(fields) and distinct(distinctFields) can be specified");
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields, support);
}
@Override
public FindByQueryWithDistinct<T> distinct(final String[] distinctFields) {
Assert.notNull(distinctFields, "distinctFields must not be null");
Assert.isNull(fields, "only one of project(fields) and distinct(distinctFields) can be specified");
// Coming from an annotation, this cannot be null.
// But a non-null but empty distinctFields means distinct on all fields
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, dFields, fields, support);
}
@Override
public Mono<T> one() {
return all().singleOrEmpty();
}
@Override
public Mono<T> first() {
return all().next();
}
@Override
public Flux<T> all() {
PseudoArgs<QueryOptions> pArgs = new PseudoArgs(template, scope, collection, options, domainType);
String statement = assembleEntityQuery(false, distinctFields, pArgs.getScope(), pArgs.getCollection());
if (LOG.isDebugEnabled()) {
LOG.debug("findByQuery {} statement: {}", pArgs, statement);
}
CouchbaseClientFactory clientFactory = template.getCouchbaseClientFactory();
ReactiveScope rs = clientFactory.withScope(pArgs.getScope()).getScope().reactive();
Mono<Object> allResult = TransactionalSupport.checkForTransactionInThreadLocalStorage().flatMap(s -> {
if (!s.isPresent()) {
QueryOptions opts = buildOptions(pArgs.getOptions());
return pArgs.getScope() == null ? clientFactory.getCluster().reactive().query(statement, opts)
: rs.query(statement, opts);
} else {
TransactionQueryOptions opts = buildTransactionOptions(pArgs.getOptions());
return (AttemptContextReactiveAccessor.createReactiveTransactionAttemptContext(s.get().getCore(),
clientFactory.getCluster().environment().jsonSerializer())).query(statement, opts);
}
});
return allResult.onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
}).flatMapMany(o -> o instanceof ReactiveQueryResult ? ((ReactiveQueryResult) o).rowsAsObject()
: Flux.fromIterable(((TransactionQueryResult) o).rowsAsObject())).flatMap(row -> {
String id = "";
Long cas = Long.valueOf(0);
if (!query.isDistinct() && distinctFields == null) {
id = row.getString(TemplateUtils.SELECT_ID);
if (id == null) {
id = row.getString(TemplateUtils.SELECT_ID_3x);
row.removeKey(TemplateUtils.SELECT_ID_3x);
}
cas = row.getLong(TemplateUtils.SELECT_CAS);
if (cas == null) {
cas = row.getLong(TemplateUtils.SELECT_CAS_3x);
row.removeKey(TemplateUtils.SELECT_CAS_3x);
}
row.removeKey(TemplateUtils.SELECT_ID);
row.removeKey(TemplateUtils.SELECT_CAS);
}
return support.decodeEntity(id, row.toString(), cas, returnType, pArgs.getScope(), pArgs.getCollection(),
null, null);
});
}
public QueryOptions buildOptions(QueryOptions options) {
QueryScanConsistency qsc = scanConsistency != null ? scanConsistency : template.getConsistency();
return query.buildQueryOptions(options, qsc);
}
private TransactionQueryOptions buildTransactionOptions(QueryOptions options) {
TransactionQueryOptions opts = OptionsBuilder.buildTransactionQueryOptions(buildOptions(options));
return opts;
}
@Override
public Mono<Long> count() {
PseudoArgs<QueryOptions> pArgs = new PseudoArgs(template, scope, collection, options, domainType);
String statement = assembleEntityQuery(true, distinctFields, pArgs.getScope(), pArgs.getCollection());
if (LOG.isDebugEnabled()) {
LOG.debug("findByQuery {} statement: {}", pArgs, statement);
}
CouchbaseClientFactory clientFactory = template.getCouchbaseClientFactory();
ReactiveScope rs = clientFactory.withScope(pArgs.getScope()).getScope().reactive();
Mono<Object> allResult = TransactionalSupport.checkForTransactionInThreadLocalStorage().flatMap(s -> {
if (!s.isPresent()) {
QueryOptions opts = buildOptions(pArgs.getOptions());
return pArgs.getScope() == null ? clientFactory.getCluster().reactive().query(statement, opts)
: rs.query(statement, opts);
} else {
TransactionQueryOptions opts = buildTransactionOptions(pArgs.getOptions());
return (AttemptContextReactiveAccessor.createReactiveTransactionAttemptContext(s.get().getCore(),
clientFactory.getCluster().environment().jsonSerializer())).query(statement, opts);
}
});
return allResult.onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
}).flatMapMany(o -> o instanceof ReactiveQueryResult ? ((ReactiveQueryResult) o).rowsAsObject()
: Flux.fromIterable(((TransactionQueryResult) o).rowsAsObject()))
.map(row -> row.getLong(row.getNames().iterator().next())).next();
}
@Override
public Mono<Boolean> exists() {
return count().map(count -> count > 0); // not efficient, just need the first one
}
private String assembleEntityQuery(final boolean count, String[] distinctFields, String scope, String collection) {
return query.toN1qlSelectString(template.getConverter(), template.getBucketName(), scope, collection,
this.domainType, this.returnType, count,
query.getDistinctFields() != null ? query.getDistinctFields() : distinctFields, fields);
}
}
}

View File

@@ -1,120 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.AnyIdReactive;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.WithGetAnyReplicaOptions;
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
/**
* Find by id from replicas Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ReactiveFindFromReplicasByIdOperation {
/**
* Loads a document from a replica.
*
* @param domainType the entity type to use for the results.
*/
<T> ReactiveFindFromReplicasById<T> findFromReplicasById(Class<T> domainType);
/**
* Terminating operations invoking the actual get execution.
*/
interface TerminatingFindFromReplicasById<T> extends AnyIdReactive<T> {
/**
* Finds one document based on the given ID.
*
* @param id the document ID.
* @return the entity if found.
*/
Mono<T> any(String id);
/**
* Finds a list of documents based on the given IDs.
*
* @param ids the document ID ids.
* @return the list of found entities.
*/
Flux<? extends T> any(Collection<String> ids);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface FindFromReplicasByIdWithOptions<T> extends TerminatingFindFromReplicasById<T>, WithGetAnyReplicaOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options options to use for execution
*/
@Override
TerminatingFindFromReplicasById<T> withOptions(GetAnyReplicaOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface FindFromReplicasByIdInCollection<T> extends FindFromReplicasByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
FindFromReplicasByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface FindFromReplicasByIdInScope<T> extends FindFromReplicasByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
FindFromReplicasByIdInCollection<T> inScope(String scope);
}
/**
* Provides methods for constructing get operations in a fluent way.
*
* @param <T> the entity type to use for the results
*/
interface ReactiveFindFromReplicasById<T> extends FindFromReplicasByIdInScope<T> {}
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import static com.couchbase.client.java.kv.GetAnyReplicaOptions.getAnyReplicaOptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.util.Assert;
import com.couchbase.client.java.codec.RawJsonTranscoder;
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
/**
* {@link ReactiveFindFromReplicasByIdOperation} implementations for Couchbase.
*
* @author Michael Reiche
*/
public class ReactiveFindFromReplicasByIdOperationSupport implements ReactiveFindFromReplicasByIdOperation {
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindFromReplicasByIdOperationSupport.class);
ReactiveFindFromReplicasByIdOperationSupport(ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ReactiveFindFromReplicasById<T> findFromReplicasById(Class<T> domainType) {
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, domainType,
OptionsBuilder.getScopeFrom(domainType), OptionsBuilder.getCollectionFrom(domainType), null,
template.support());
}
static class ReactiveFindFromReplicasByIdSupport<T> implements ReactiveFindFromReplicasById<T> {
private final ReactiveCouchbaseTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final String scope;
private final String collection;
private final GetAnyReplicaOptions options;
private final ReactiveTemplateSupport support;
ReactiveFindFromReplicasByIdSupport(ReactiveCouchbaseTemplate template, Class<?> domainType, Class<T> returnType,
String scope, String collection, GetAnyReplicaOptions options, ReactiveTemplateSupport support) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.support = support;
}
@Override
public Mono<T> any(final String id) {
GetAnyReplicaOptions garOptions = options != null ? options : getAnyReplicaOptions();
if (garOptions.build().transcoder() == null) {
garOptions.transcoder(RawJsonTranscoder.INSTANCE);
}
PseudoArgs<GetAnyReplicaOptions> pArgs = new PseudoArgs<>(template, scope, collection, garOptions, domainType);
if (LOG.isDebugEnabled()) {
LOG.debug("getAnyReplica key={} {}", id, pArgs);
}
return TransactionalSupport.verifyNotInTransaction("findFromReplicasById").then(Mono.just(id))
.flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
.getCollection(pArgs.getCollection()).reactive().getAnyReplica(docId, pArgs.getOptions()))
.flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), returnType,
pArgs.getScope(), pArgs.getCollection(), null, null))
.onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
});
}
@Override
public Flux<? extends T> any(Collection<String> ids) {
return Flux.fromIterable(ids).flatMap(this::any);
}
@Override
public TerminatingFindFromReplicasById<T> withOptions(final GetAnyReplicaOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options,
support);
}
@Override
public FindFromReplicasByIdWithOptions<T> inCollection(final String collection) {
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType, scope,
collection != null ? collection : this.collection, options, support);
}
@Override
public FindFromReplicasByIdInCollection<T> inScope(final String scope) {
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType,
scope != null ? scope : this.scope, collection, options, support);
}
}
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
/**
* The fluent couchbase operations combines all different possible operations for simplicity reasons.
*/
public interface ReactiveFluentCouchbaseOperations extends ReactiveUpsertByIdOperation, ReactiveInsertByIdOperation,
ReactiveReplaceByIdOperation, ReactiveFindByIdOperation, ReactiveExistsByIdOperation,
ReactiveFindByAnalyticsOperation, ReactiveFindFromReplicasByIdOperation, ReactiveFindByQueryOperation,
ReactiveRemoveByIdOperation, ReactiveRemoveByQueryOperation {}

View File

@@ -1,136 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllEntityReactive;
import org.springframework.data.couchbase.core.support.WithDurability;
import org.springframework.data.couchbase.core.support.WithExpiry;
import org.springframework.data.couchbase.core.support.WithInsertOptions;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.InsertOptions;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* Insert Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ReactiveInsertByIdOperation {
/**
* Insert using the KV service.
*
* @param domainType the entity type to insert.
*/
<T> ReactiveInsertById<T> insertById(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingInsertById<T> extends OneAndAllEntityReactive<T> {
/**
* Insert one entity.
*
* @return Inserted entity.
*/
@Override
Mono<T> one(T object);
/**
* Insert a collection of entities.
*
* @return Inserted entities
*/
@Override
Flux<? extends T> all(Collection<? extends T> objects);
}
/**
* Fluent method to specify options.
*/
interface InsertByIdWithOptions<T> extends TerminatingInsertById<T>, WithInsertOptions<T> {
/**
* Fluent method to specify options to use for execution.
*
* @param options to use for execution
*/
@Override
TerminatingInsertById<T> withOptions(InsertOptions options);
}
/**
* Fluent method to specify the collection.
*/
interface InsertByIdInCollection<T> extends InsertByIdWithOptions<T>, InCollection<T> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
InsertByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*/
interface InsertByIdInScope<T> extends InsertByIdInCollection<T>, InScope<T> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
InsertByIdInCollection<T> inScope(String scope);
}
interface InsertByIdWithDurability<T> extends InsertByIdInScope<T>, WithDurability<T> {
@Override
InsertByIdInScope<T> withDurability(DurabilityLevel durabilityLevel);
@Override
InsertByIdInScope<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
}
interface InsertByIdWithExpiry<T> extends InsertByIdWithDurability<T>, WithExpiry<T> {
@Override
InsertByIdWithDurability<T> withExpiry(Duration expiry);
}
/**
* Provides methods for constructing KV insert operations in a fluent way.
*
* @param <T> the entity type to insert
*/
interface ReactiveInsertById<T> extends InsertByIdWithExpiry<T> {}
}

View File

@@ -1,191 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import static com.couchbase.client.java.transactions.internal.ConverterUtil.makeCollectionIdentifier;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.InsertOptions;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* {@link ReactiveInsertByIdOperation} implementations for Couchbase.
*
* @author Michael Reiche
*/
public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOperation {
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveInsertByIdOperationSupport.class);
public ReactiveInsertByIdOperationSupport(final ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ReactiveInsertById<T> insertById(final Class<T> domainType) {
Assert.notNull(domainType, "DomainType must not be null!");
return new ReactiveInsertByIdSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
null, template.support());
}
static class ReactiveInsertByIdSupport<T> implements ReactiveInsertById<T> {
private final ReactiveCouchbaseTemplate template;
private final Class<T> domainType;
private final String scope;
private final String collection;
private final InsertOptions options;
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveTemplateSupport support;
ReactiveInsertByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final String scope,
final String collection, final InsertOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
final DurabilityLevel durabilityLevel, Duration expiry, ReactiveTemplateSupport support) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.support = support;
}
@Override
public Mono<T> one(T object) {
PseudoArgs<InsertOptions> pArgs = new PseudoArgs(template, scope, collection, options, domainType);
if (LOG.isDebugEnabled()) {
LOG.debug("insertById object={} {}", object, pArgs);
}
return Mono
.just(template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getCollection(pArgs.getCollection()))
.flatMap(collection -> support.encodeEntity(object)
.flatMap(converted -> TransactionalSupport.checkForTransactionInThreadLocalStorage().flatMap(ctxOpt -> {
if (!ctxOpt.isPresent()) {
return collection.reactive()
.insert(converted.getId(), converted.export(), buildOptions(pArgs.getOptions(), converted))
.flatMap(result -> this.support.applyResult(object, converted, converted.getId(), result.cas(),
null, null));
} else {
rejectInvalidTransactionalOptions();
return ctxOpt.get().getCore()
.insert(makeCollectionIdentifier(collection.async()), converted.getId(),
template.getCouchbaseClientFactory().getCluster().environment().transcoder()
.encode(converted.export()).encoded())
.flatMap(result -> this.support.applyResult(object, converted, converted.getId(), result.cas(),
null, null));
}
})).onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
}));
}
private void rejectInvalidTransactionalOptions() {
if ((this.persistTo != null && this.persistTo != PersistTo.NONE)
|| (this.replicateTo != null && this.replicateTo != ReplicateTo.NONE)) {
throw new IllegalArgumentException(
"withDurability PersistTo and ReplicateTo overload is not supported in a transaction");
}
if (this.expiry != null) {
throw new IllegalArgumentException("withExpiry is not supported in a transaction");
}
if (this.durabilityLevel != null && this.durabilityLevel != DurabilityLevel.NONE) {
throw new IllegalArgumentException("withDurability is not supported in a transaction");
}
if (this.options != null) {
throw new IllegalArgumentException("withOptions is not supported in a transaction");
}
}
@Override
public Flux<? extends T> all(Collection<? extends T> objects) {
return Flux.fromIterable(objects).flatMap(this::one);
}
public InsertOptions buildOptions(InsertOptions options, CouchbaseDocument doc) { // CouchbaseDocument converted
return OptionsBuilder.buildInsertOptions(options, persistTo, replicateTo, durabilityLevel, expiry, doc);
}
@Override
public TerminatingInsertById<T> withOptions(final InsertOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public InsertByIdInCollection<T> inScope(final String scope) {
return new ReactiveInsertByIdSupport<>(template, domainType, scope != null ? scope : this.scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry, support);
}
@Override
public InsertByIdWithOptions<T> inCollection(final String collection) {
return new ReactiveInsertByIdSupport<>(template, domainType, scope,
collection != null ? collection : this.collection, options, persistTo, replicateTo, durabilityLevel, expiry,
support);
}
@Override
public InsertByIdInScope<T> withDurability(final DurabilityLevel durabilityLevel) {
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public InsertByIdInScope<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
Assert.notNull(persistTo, "PersistTo must not be null.");
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public InsertByIdWithDurability<T> withExpiry(final Duration expiry) {
Assert.notNull(expiry, "expiry must not be null.");
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
}
}

View File

@@ -1,149 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllIdReactive;
import org.springframework.data.couchbase.core.support.WithDurability;
import org.springframework.data.couchbase.core.support.WithRemoveOptions;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.RemoveOptions;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* Remove Operations on KV service.
*
* @author Christoph Strobl
* @author Michael Reiche
* @since 2.0
*/
public interface ReactiveRemoveByIdOperation {
/**
* Removes a document.
*/
@Deprecated
ReactiveRemoveById removeById();
/**
* Removes a document.
*/
ReactiveRemoveById removeById(Class<?> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingRemoveById extends OneAndAllIdReactive<RemoveResult> {
/**
* Remove one document based on the given ID.
*
* @param id the document ID.
* @return result of the remove
*/
@Override
Mono<RemoveResult> one(String id);
/**
* Remove one document. Requires whole entity for transaction to have the cas.
*
* @param entity the entity
* @return result of the remove
*/
Mono<RemoveResult> oneEntity(Object entity);
/**
* Remove the documents in the collection.
*
* @param ids the document IDs.
* @return result of the removes.
*/
@Override
Flux<RemoveResult> all(Collection<String> ids);
/**
* Remove the documents in the collection. Requires whole entity for transaction to have the cas.
*
* @param entities the entities to remove.
* @return result of the removes.
*/
Flux<RemoveResult> allEntities(Collection<Object> entities);
}
/**
* Fluent method to specify options.
*/
interface RemoveByIdWithOptions extends TerminatingRemoveById, WithRemoveOptions<RemoveResult> {
/**
* Fluent method to specify options to use for execution
*
* @param options options to use for execution
*/
TerminatingRemoveById withOptions(RemoveOptions options);
}
/**
* Fluent method to specify the collection.
*/
interface RemoveByIdInCollection extends RemoveByIdWithOptions, InCollection<Object> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
RemoveByIdWithOptions inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*/
interface RemoveByIdInScope extends RemoveByIdInCollection, InScope<Object> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
RemoveByIdInCollection inScope(String scope);
}
interface RemoveByIdWithDurability extends RemoveByIdInScope, WithDurability<RemoveResult> {
@Override
RemoveByIdInScope withDurability(DurabilityLevel durabilityLevel);
@Override
RemoveByIdInScope withDurability(PersistTo persistTo, ReplicateTo replicateTo);
}
interface RemoveByIdWithCas extends RemoveByIdWithDurability {
RemoveByIdWithDurability withCas(Long cas);
}
/**
* Provides methods for constructing remove operations in a fluent way.
*/
interface ReactiveRemoveById extends RemoveByIdWithCas {}
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import static com.couchbase.client.java.transactions.internal.ConverterUtil.makeCollectionIdentifier;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.core.transaction.CoreTransactionAttemptContext;
import com.couchbase.client.core.transaction.CoreTransactionGetResult;
import com.couchbase.client.java.ReactiveCollection;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.RemoveOptions;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* {@link ReactiveRemoveByIdOperation} implementations for Couchbase.
*
* @author Michael Reiche
*/
public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOperation {
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveRemoveByIdOperationSupport.class);
public ReactiveRemoveByIdOperationSupport(final ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
@Deprecated
public ReactiveRemoveById removeById() {
return removeById(null);
}
@Override
public ReactiveRemoveById removeById(Class<?> domainType) {
return new ReactiveRemoveByIdSupport(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
null);
}
static class ReactiveRemoveByIdSupport implements ReactiveRemoveById {
private final ReactiveCouchbaseTemplate template;
private final Class<?> domainType;
private final String scope;
private final String collection;
private final RemoveOptions options;
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Long cas;
ReactiveRemoveByIdSupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType, final String scope,
final String collection, final RemoveOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
final DurabilityLevel durabilityLevel, Long cas) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.cas = cas;
}
@Override
public Mono<RemoveResult> one(final String id) {
PseudoArgs<RemoveOptions> pArgs = new PseudoArgs<>(template, scope, collection, options, domainType);
if (LOG.isDebugEnabled()) {
LOG.debug("removeById key={} {}", id, pArgs);
}
CouchbaseClientFactory clientFactory = template.getCouchbaseClientFactory();
ReactiveCollection rc = clientFactory.withScope(pArgs.getScope()).getCollection(pArgs.getCollection()).reactive();
return TransactionalSupport.checkForTransactionInThreadLocalStorage().flatMap(s -> {
if (!s.isPresent()) {
return rc.remove(id, buildRemoveOptions(pArgs.getOptions())).map(r -> RemoveResult.from(id, r));
} else {
rejectInvalidTransactionalOptions();
if (cas == null || cas == 0) {
throw new IllegalArgumentException("cas must be supplied for tx remove");
}
CoreTransactionAttemptContext ctx = s.get().getCore();
Mono<CoreTransactionGetResult> gr = ctx.get(makeCollectionIdentifier(rc.async()), id);
return gr.flatMap(getResult -> {
if (getResult.cas() != cas) {
return Mono.error(TransactionalSupport.retryTransactionOnCasMismatch(ctx, getResult.cas(), cas));
}
return ctx.remove(getResult).map(r -> new RemoveResult(id, 0, null));
});
}
}).onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
});
}
private void rejectInvalidTransactionalOptions() {
if ((this.persistTo != null && this.persistTo != PersistTo.NONE)
|| (this.replicateTo != null && this.replicateTo != ReplicateTo.NONE)) {
throw new IllegalArgumentException(
"withDurability PersistTo and ReplicateTo overload is not supported in a transaction");
}
if (this.durabilityLevel != null && this.durabilityLevel != DurabilityLevel.NONE) {
throw new IllegalArgumentException("withDurability is not supported in a transaction");
}
if (this.options != null) {
throw new IllegalArgumentException("withOptions is not supported in a transaction");
}
}
@Override
public Mono<RemoveResult> oneEntity(Object entity) {
ReactiveRemoveByIdSupport op = new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options,
persistTo, replicateTo, durabilityLevel, template.support().getCas(entity));
return op.one(template.support().getId(entity).toString());
}
@Override
public Flux<RemoveResult> all(final Collection<String> ids) {
return Flux.fromIterable(ids).flatMap(this::one);
}
@Override
public Flux<RemoveResult> allEntities(Collection<Object> entities) {
return Flux.fromIterable(entities).flatMap(this::oneEntity);
}
private RemoveOptions buildRemoveOptions(RemoveOptions options) {
return OptionsBuilder.buildRemoveOptions(options, persistTo, replicateTo, durabilityLevel, cas);
}
@Override
public RemoveByIdInScope withDurability(final DurabilityLevel durabilityLevel) {
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, cas);
}
@Override
public RemoveByIdInScope withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
Assert.notNull(persistTo, "PersistTo must not be null.");
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, cas);
}
@Override
public RemoveByIdWithDurability inCollection(final String collection) {
return new ReactiveRemoveByIdSupport(template, domainType, scope,
collection != null ? collection : this.collection, options, persistTo, replicateTo, durabilityLevel, cas);
}
@Override
public RemoveByIdInCollection inScope(final String scope) {
return new ReactiveRemoveByIdSupport(template, domainType, scope != null ? scope : this.scope, collection,
options, persistTo, replicateTo, durabilityLevel, cas);
}
@Override
public TerminatingRemoveById withOptions(final RemoveOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, cas);
}
@Override
public RemoveByIdWithDurability withCas(Long cas) {
return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, cas);
}
}
}

View File

@@ -1,133 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.WithConsistency;
import org.springframework.data.couchbase.core.support.WithQuery;
import org.springframework.data.couchbase.core.support.WithQueryOptions;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* RemoveBy Query Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ReactiveRemoveByQueryOperation {
/**
* Remove via the query service.
*/
<T> ReactiveRemoveByQuery<T> removeByQuery(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingRemoveByQuery<T> {
/**
* Remove all matching documents.
*
* @return RemoveResult for each matching document
*/
Flux<RemoveResult> all();
}
/**
* Fluent methods to specify the query
*
* @param <T> the entity type.
*/
interface RemoveByQueryWithQuery<T> extends TerminatingRemoveByQuery<T>, WithQuery<RemoveResult> {
TerminatingRemoveByQuery<T> matching(Query query);
default TerminatingRemoveByQuery<T> matching(QueryCriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface RemoveByQueryWithOptions<T> extends RemoveByQueryWithQuery<T>, WithQueryOptions<RemoveResult> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
RemoveByQueryWithQuery<T> withOptions(QueryOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface RemoveByQueryInCollection<T> extends RemoveByQueryWithOptions<T>, InCollection<Object> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
RemoveByQueryWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface RemoveByQueryInScope<T> extends RemoveByQueryInCollection<T>, InScope<Object> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
RemoveByQueryInCollection<T> inScope(String scope);
}
@Deprecated
interface RemoveByQueryConsistentWith<T> extends RemoveByQueryInScope<T> {
@Deprecated
RemoveByQueryInScope<T> consistentWith(QueryScanConsistency scanConsistency);
}
interface RemoveByQueryWithConsistency<T> extends RemoveByQueryConsistentWith<T>, WithConsistency<RemoveResult> {
@Override
RemoveByQueryConsistentWith<T> withConsistency(QueryScanConsistency scanConsistency);
}
/**
* Provides methods for constructing query operations in a fluent way.
*
* @param <T> the entity type.
*/
interface ReactiveRemoveByQuery<T> extends RemoveByQueryWithConsistency<T> {}
}

View File

@@ -1,166 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.data.couchbase.core.support.TemplateUtils;
import org.springframework.util.Assert;
import com.couchbase.client.core.deps.com.fasterxml.jackson.databind.node.ObjectNode;
import com.couchbase.client.java.ReactiveScope;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
import com.couchbase.client.java.query.ReactiveQueryResult;
import com.couchbase.client.java.transactions.TransactionQueryOptions;
/**
* {@link ReactiveRemoveByQueryOperation} implementations for Couchbase.
*
* @author Michael Reiche
*/
public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQueryOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveRemoveByQueryOperationSupport.class);
public ReactiveRemoveByQueryOperationSupport(final ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ReactiveRemoveByQuery<T> removeByQuery(Class<T> domainType) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, ALL_QUERY, null,
OptionsBuilder.getScopeFrom(domainType), OptionsBuilder.getCollectionFrom(domainType), null);
}
static class ReactiveRemoveByQuerySupport<T> implements ReactiveRemoveByQuery<T> {
private final ReactiveCouchbaseTemplate template;
private final Class<T> domainType;
private final Query query;
private final QueryScanConsistency scanConsistency;
private final String scope;
private final String collection;
private final QueryOptions options;
ReactiveRemoveByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final Query query,
final QueryScanConsistency scanConsistency, String scope, String collection, QueryOptions options) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.scanConsistency = scanConsistency;
this.scope = scope;
this.collection = collection;
this.options = options;
}
@Override
public Flux<RemoveResult> all() {
PseudoArgs<QueryOptions> pArgs = new PseudoArgs<>(template, scope, collection, options, domainType);
String statement = assembleDeleteQuery(pArgs.getScope(), pArgs.getCollection());
if (LOG.isDebugEnabled()) {
LOG.debug("removeByQuery {} statement: {}", pArgs, statement);
}
CouchbaseClientFactory clientFactory = template.getCouchbaseClientFactory();
ReactiveScope rs = clientFactory.withScope(pArgs.getScope()).getScope().reactive();
return TransactionalSupport.checkForTransactionInThreadLocalStorage().flatMapMany(transactionContext -> {
if (!transactionContext.isPresent()) {
QueryOptions opts = buildQueryOptions(pArgs.getOptions());
return (pArgs.getScope() == null ? clientFactory.getCluster().reactive().query(statement, opts)
: rs.query(statement, opts)).flatMapMany(ReactiveQueryResult::rowsAsObject)
.map(row -> new RemoveResult(row.getString(TemplateUtils.SELECT_ID),
row.getLong(TemplateUtils.SELECT_CAS), Optional.empty()));
} else {
TransactionQueryOptions opts = OptionsBuilder
.buildTransactionQueryOptions(buildQueryOptions(pArgs.getOptions()));
ObjectNode convertedOptions = com.couchbase.client.java.transactions.internal.OptionsUtil
.createTransactionOptions(pArgs.getScope() == null ? null : rs, statement, opts);
return transactionContext.get().getCore()
.queryBlocking(statement, template.getBucketName(), pArgs.getScope(), convertedOptions, false)
.flatMapIterable(result -> result.rows).map(row -> {
JsonObject json = JsonObject.fromJson(row.data());
return new RemoveResult(json.getString(TemplateUtils.SELECT_ID), json.getLong(TemplateUtils.SELECT_CAS),
Optional.empty());
});
}
});
}
private QueryOptions buildQueryOptions(QueryOptions options) {
QueryScanConsistency qsc = scanConsistency != null ? scanConsistency : template.getConsistency();
return query.buildQueryOptions(options, qsc);
}
@Override
public TerminatingRemoveByQuery<T> matching(final Query query) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
options);
}
@Override
public RemoveByQueryWithConsistency<T> inCollection(final String collection) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope,
collection != null ? collection : this.collection, options);
}
@Override
@Deprecated
public RemoveByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
options);
}
@Override
public RemoveByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
options);
}
private String assembleDeleteQuery(String scope, String collection) {
return query.toN1qlRemoveString(template.getConverter(), template.getBucketName(), scope, collection,
this.domainType);
}
@Override
public RemoveByQueryWithQuery<T> withOptions(final QueryOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
options);
}
@Override
public RemoveByQueryInCollection<T> inScope(final String scope) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency,
scope != null ? scope : this.scope, collection, options);
}
}
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllEntityReactive;
import org.springframework.data.couchbase.core.support.WithDurability;
import org.springframework.data.couchbase.core.support.WithExpiry;
import org.springframework.data.couchbase.core.support.WithReplaceOptions;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplaceOptions;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* ReplaceOperations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ReactiveReplaceByIdOperation {
/**
* Replace using the KV service.
*
* @param domainType the entity type to replace.
*/
<T> ReactiveReplaceById<T> replaceById(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingReplaceById<T> extends OneAndAllEntityReactive<T> {
/**
* Replace one entity.
*
* @return Replaced entity.
*/
Mono<T> one(T object);
/**
* Replace a collection of entities.
*
* @return Replaced entities
*/
Flux<? extends T> all(Collection<? extends T> objects);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use for the results.
*/
interface ReplaceByIdWithOptions<T> extends TerminatingReplaceById<T>, WithReplaceOptions<RemoveResult> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
@Override
TerminatingReplaceById<T> withOptions(ReplaceOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface ReplaceByIdInCollection<T> extends ReplaceByIdWithOptions<T>, InCollection<Object> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
ReplaceByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface ReplaceByIdInScope<T> extends ReplaceByIdInCollection<T>, InScope<Object> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
ReplaceByIdInCollection<T> inScope(String scope);
}
interface ReplaceByIdWithDurability<T> extends ReplaceByIdInScope<T>, WithDurability<T> {
ReplaceByIdInScope<T> withDurability(DurabilityLevel durabilityLevel);
ReplaceByIdInScope<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
}
interface ReplaceByIdWithExpiry<T> extends ReplaceByIdWithDurability<T>, WithExpiry<T> {
ReplaceByIdWithDurability<T> withExpiry(final Duration expiry);
}
/**
* Provides methods for constructing KV replace operations in a fluent way.
*
* @param <T> the entity type to replace
*/
interface ReactiveReplaceById<T> extends ReplaceByIdWithExpiry<T> {}
}

View File

@@ -1,211 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import static com.couchbase.client.java.transactions.internal.ConverterUtil.makeCollectionIdentifier;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.util.Assert;
import com.couchbase.client.core.io.CollectionIdentifier;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.core.transaction.CoreTransactionAttemptContext;
import com.couchbase.client.core.transaction.CoreTransactionGetResult;
import com.couchbase.client.core.transaction.util.DebugUtil;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplaceOptions;
import com.couchbase.client.java.kv.ReplicateTo;
/**
* {@link ReactiveReplaceByIdOperation} implementations for Couchbase.
*
* @author Michael Reiche
*/
public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdOperation {
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveReplaceByIdOperationSupport.class);
public ReactiveReplaceByIdOperationSupport(final ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ReactiveReplaceById<T> replaceById(final Class<T> domainType) {
Assert.notNull(domainType, "DomainType must not be null!");
return new ReactiveReplaceByIdSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
null, template.support());
}
static class ReactiveReplaceByIdSupport<T> implements ReactiveReplaceById<T> {
private final ReactiveCouchbaseTemplate template;
private final Class<T> domainType;
private final String scope;
private final String collection;
private final ReplaceOptions options;
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveTemplateSupport support;
ReactiveReplaceByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final String scope,
final String collection, final ReplaceOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
final DurabilityLevel durabilityLevel, final Duration expiry, ReactiveTemplateSupport support) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.support = support;
}
@Override
public Mono<T> one(T object) {
PseudoArgs<ReplaceOptions> pArgs = new PseudoArgs<>(template, scope, collection, options, domainType);
if (LOG.isDebugEnabled()) {
LOG.debug("replaceById object={} {}", object, pArgs);
}
return Mono
.just(template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getCollection(pArgs.getCollection()))
.flatMap(collection -> support.encodeEntity(object)
.flatMap(converted -> TransactionalSupport.checkForTransactionInThreadLocalStorage().flatMap(ctxOpt -> {
if (!ctxOpt.isPresent()) {
return collection.reactive()
.replace(converted.getId(), converted.export(),
buildReplaceOptions(pArgs.getOptions(), object, converted))
.flatMap(result -> support.applyResult(object, converted, converted.getId(), result.cas(), null,
null));
} else {
rejectInvalidTransactionalOptions();
Long cas = support.getCas(object);
if (cas == null || cas == 0) {
throw new IllegalArgumentException(
"cas must be supplied in object for tx replace. object=" + object);
}
CollectionIdentifier collId = makeCollectionIdentifier(collection.async());
CoreTransactionAttemptContext ctx = ctxOpt.get().getCore();
ctx.logger().info(ctx.attemptId(), "refetching %s for Spring replace",
DebugUtil.docId(collId, converted.getId()));
Mono<CoreTransactionGetResult> gr = ctx.get(collId, converted.getId());
return gr.flatMap(getResult -> {
if (getResult.cas() != cas) {
return Mono.error(TransactionalSupport.retryTransactionOnCasMismatch(ctx, getResult.cas(), cas));
}
return ctx.replace(getResult, template.getCouchbaseClientFactory().getCluster().environment()
.transcoder().encode(converted.export()).encoded());
}).flatMap(result -> support.applyResult(object, converted, converted.getId(), result.cas(), null, null));
}
})).onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
}));
}
private void rejectInvalidTransactionalOptions() {
if ((this.persistTo != null && this.persistTo != PersistTo.NONE)
|| (this.replicateTo != null && this.replicateTo != ReplicateTo.NONE)) {
throw new IllegalArgumentException(
"withDurability PersistTo and ReplicateTo overload is not supported in a transaction");
}
if (this.expiry != null) {
throw new IllegalArgumentException("withExpiry is not supported in a transaction");
}
if (this.durabilityLevel != null && this.durabilityLevel != DurabilityLevel.NONE) {
throw new IllegalArgumentException("withDurability is not supported in a transaction");
}
if (this.options != null) {
throw new IllegalArgumentException("withOptions is not supported in a transaction");
}
}
@Override
public Flux<? extends T> all(Collection<? extends T> objects) {
return Flux.fromIterable(objects).flatMap(this::one);
}
private ReplaceOptions buildReplaceOptions(ReplaceOptions options, T object, CouchbaseDocument doc) {
return OptionsBuilder.buildReplaceOptions(options, persistTo, replicateTo, durabilityLevel, expiry,
support.getCas(object), doc);
}
@Override
public TerminatingReplaceById<T> withOptions(final ReplaceOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public ReplaceByIdWithDurability<T> inCollection(final String collection) {
return new ReactiveReplaceByIdSupport<>(template, domainType, scope,
collection != null ? collection : this.collection, options, persistTo, replicateTo, durabilityLevel, expiry,
support);
}
@Override
public ReplaceByIdInCollection<T> inScope(final String scope) {
return new ReactiveReplaceByIdSupport<>(template, domainType, scope != null ? scope : this.scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry, support);
}
@Override
public ReplaceByIdInScope<T> withDurability(final DurabilityLevel durabilityLevel) {
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public ReplaceByIdInScope<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
Assert.notNull(persistTo, "PersistTo must not be null.");
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public ReplaceByIdWithDurability<T> withExpiry(final Duration expiry) {
Assert.notNull(expiry, "expiry must not be null.");
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2021-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Mono;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.transaction.CouchbaseResourceHolder;
/**
* ReactiveTemplateSupport
*
* @author Michael Reiche
*/
public interface ReactiveTemplateSupport {
Mono<CouchbaseDocument> encodeEntity(Object entityToEncode);
<T> Mono<T> decodeEntity(String id, String source, Long cas, Class<T> entityClass, String scope, String collection,
Object txResultHolder, CouchbaseResourceHolder holder);
<T> Mono<T> applyResult(T entity, CouchbaseDocument converted, Object id, Long cas,
Object txResultHolder, CouchbaseResourceHolder holder);
Long getCas(Object entity);
Object getId(Object entity);
String getJavaNameForEntity(Class<?> clazz);
TranslationService getTranslationService();
}

View File

@@ -1,140 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.OneAndAllEntityReactive;
import org.springframework.data.couchbase.core.support.WithDurability;
import org.springframework.data.couchbase.core.support.WithExpiry;
import org.springframework.data.couchbase.core.support.WithUpsertOptions;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
import com.couchbase.client.java.kv.UpsertOptions;
/**
* Upsert Operations
*
* @author Christoph Strobl
* @since 2.0
*/
public interface ReactiveUpsertByIdOperation {
/**
* Upsert using the KV service.
*
* @param domainType the entity type to upsert.
*/
<T> ReactiveUpsertById<T> upsertById(Class<T> domainType);
/**
* Terminating operations invoking the actual execution.
*/
interface TerminatingUpsertById<T> extends OneAndAllEntityReactive<T> {
/**
* Upsert one entity.
*
* @return Upserted entity.
*/
@Override
Mono<T> one(T object);
/**
* Insert a collection of entities.
*
* @return Inserted entities
*/
@Override
Flux<? extends T> all(Collection<? extends T> objects);
}
/**
* Fluent method to specify options.
*
* @param <T> the entity type to use.
*/
interface UpsertByIdWithOptions<T> extends TerminatingUpsertById<T>, WithUpsertOptions<T> {
/**
* Fluent method to specify options to use for execution
*
* @param options to use for execution
*/
@Override
TerminatingUpsertById<T> withOptions(UpsertOptions options);
}
/**
* Fluent method to specify the collection.
*
* @param <T> the entity type to use for the results.
*/
interface UpsertByIdInCollection<T> extends UpsertByIdWithOptions<T>, InCollection<Object> {
/**
* With a different collection
*
* @param collection the collection to use.
*/
@Override
UpsertByIdWithOptions<T> inCollection(String collection);
}
/**
* Fluent method to specify the scope.
*
* @param <T> the entity type to use for the results.
*/
interface UpsertByIdInScope<T> extends UpsertByIdInCollection<T>, InScope<Object> {
/**
* With a different scope
*
* @param scope the scope to use.
*/
@Override
UpsertByIdInCollection<T> inScope(String scope);
}
interface UpsertByIdWithDurability<T> extends UpsertByIdInScope<T>, WithDurability<T> {
@Override
UpsertByIdInScope<T> withDurability(DurabilityLevel durabilityLevel);
@Override
UpsertByIdInScope<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
}
interface UpsertByIdWithExpiry<T> extends UpsertByIdWithDurability<T>, WithExpiry<T> {
@Override
UpsertByIdWithDurability<T> withExpiry(Duration expiry);
}
/**
* Provides methods for constructing KV operations in a fluent way.
*
* @param <T> the entity type to upsert
*/
interface ReactiveUpsertById<T> extends UpsertByIdWithExpiry<T> {}
}

View File

@@ -1,164 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
import com.couchbase.client.java.kv.UpsertOptions;
/**
* {@link ReactiveUpsertByIdOperation} implementations for Couchbase.
*
* @author Michael Reiche
*/
public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOperation {
private final ReactiveCouchbaseTemplate template;
private static final Logger LOG = LoggerFactory.getLogger(ReactiveUpsertByIdOperationSupport.class);
public ReactiveUpsertByIdOperationSupport(final ReactiveCouchbaseTemplate template) {
this.template = template;
}
@Override
public <T> ReactiveUpsertById<T> upsertById(final Class<T> domainType) {
Assert.notNull(domainType, "DomainType must not be null!");
return new ReactiveUpsertByIdSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType),
OptionsBuilder.getCollectionFrom(domainType), null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
null, template.support());
}
static class ReactiveUpsertByIdSupport<T> implements ReactiveUpsertById<T> {
private final ReactiveCouchbaseTemplate template;
private final Class<T> domainType;
private final String scope;
private final String collection;
private final UpsertOptions options;
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveTemplateSupport support;
ReactiveUpsertByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final String scope,
final String collection, final UpsertOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
final DurabilityLevel durabilityLevel, final Duration expiry, ReactiveTemplateSupport support) {
this.template = template;
this.domainType = domainType;
this.scope = scope;
this.collection = collection;
this.options = options;
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.support = support;
}
@Override
public Mono<T> one(T object) {
PseudoArgs<UpsertOptions> pArgs = new PseudoArgs(template, scope, collection, options, domainType);
if (LOG.isDebugEnabled()) {
LOG.debug("upsertById object={} {}", object, pArgs);
}
Mono<T> reactiveEntity = TransactionalSupport.verifyNotInTransaction("upsertById")
.then(support.encodeEntity(object)).flatMap(converted -> {
return Mono
.just(template.getCouchbaseClientFactory().withScope(pArgs.getScope())
.getCollection(pArgs.getCollection()))
.flatMap(collection -> collection.reactive()
.upsert(converted.getId(), converted.export(), buildUpsertOptions(pArgs.getOptions(), converted))
.flatMap(
result -> support.applyResult(object, converted, converted.getId(), result.cas(), null, null)));
});
return reactiveEntity.onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
} else {
return throwable;
}
});
}
@Override
public Flux<? extends T> all(Collection<? extends T> objects) {
return Flux.fromIterable(objects).flatMap(this::one);
}
private UpsertOptions buildUpsertOptions(UpsertOptions options, CouchbaseDocument doc) {
return OptionsBuilder.buildUpsertOptions(options, persistTo, replicateTo, durabilityLevel, expiry, doc);
}
@Override
public TerminatingUpsertById<T> withOptions(final UpsertOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveUpsertByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public UpsertByIdWithDurability<T> inCollection(final String collection) {
return new ReactiveUpsertByIdSupport<>(template, domainType, scope,
collection != null ? collection : this.collection, options, persistTo, replicateTo, durabilityLevel, expiry,
support);
}
@Override
public UpsertByIdInCollection<T> inScope(final String scope) {
return new ReactiveUpsertByIdSupport<>(template, domainType, scope != null ? scope : this.scope, collection,
options, persistTo, replicateTo, durabilityLevel, expiry, support);
}
@Override
public UpsertByIdInScope<T> withDurability(final DurabilityLevel durabilityLevel) {
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public UpsertByIdInScope<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
Assert.notNull(persistTo, "PersistTo must not be null.");
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
@Override
public UpsertByIdWithDurability<T> withExpiry(final Duration expiry) {
Assert.notNull(expiry, "expiry must not be null.");
return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
durabilityLevel, expiry, support);
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import java.util.Objects;
import java.util.Optional;
import com.couchbase.client.core.msg.kv.MutationToken;
import com.couchbase.client.java.kv.MutationResult;
public class RemoveResult {
private final String id;
private final long cas;
private final Optional<MutationToken> mutationToken;
public RemoveResult(String id, long cas, Optional<MutationToken> mutationToken) {
this.id = id;
this.cas = cas;
this.mutationToken = mutationToken;
}
public static RemoveResult from(final String id, final MutationResult result) {
return new RemoveResult(id, result.cas(), result.mutationToken());
}
public String getId() {
return id;
}
public long getCas() {
return cas;
}
public Optional<MutationToken> getMutationToken() {
return mutationToken;
}
@Override
public String toString() {
return "RemoveResult{" + "id='" + id + '\'' + ", cas=" + cas + ", mutationToken=" + mutationToken + '}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
RemoveResult that = (RemoveResult) o;
return cas == that.cas && Objects.equals(id, that.id) && Objects.equals(mutationToken, that.mutationToken);
}
@Override
public int hashCode() {
return Objects.hash(id, cas, mutationToken);
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2021-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.transaction.CouchbaseResourceHolder;
/**
* @author Michael Reiche
*/
public interface TemplateSupport {
CouchbaseDocument encodeEntity(Object entityToEncode);
<T> T decodeEntity(String id, String source, Long cas, Class<T> entityClass, String scope, String collection,
Object txResultHolder, CouchbaseResourceHolder holder);
<T> T applyResult(T entity, CouchbaseDocument converted, Object id, long cas, Object txResultHolder,
CouchbaseResourceHolder holder);
Long getCas(Object entity);
Object getId(Object entity);
String getJavaNameForEntity(Class<?> clazz);
<T> Integer getTxResultHolder(T source);
TranslationService getTranslationService();
}

View File

@@ -1,75 +0,0 @@
/*
* Copyright 2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import reactor.core.publisher.Mono;
import java.util.Optional;
import org.springframework.data.couchbase.transaction.CouchbaseResourceHolder;
import com.couchbase.client.core.annotation.Stability;
import com.couchbase.client.core.error.CasMismatchException;
import com.couchbase.client.core.error.transaction.TransactionOperationFailedException;
import com.couchbase.client.core.transaction.CoreTransactionAttemptContext;
import com.couchbase.client.core.transaction.threadlocal.TransactionMarkerOwner;
/**
* Utility methods to support transactions.
*
* @author Graham Pople
*/
@Stability.Internal
public class TransactionalSupport {
/**
* Returns non-empty iff in a transaction. It determines this from thread-local storage and/or reactive context.
* <p>
* The user could be doing a reactive operation (with .block()) inside a blocking transaction (like @Transactional).
* Or a blocking operation inside a ReactiveTransactionsWrapper transaction (which would be a bad idea). So, need to
* check both thread-local storage and reactive context.
*/
public static Mono<Optional<CouchbaseResourceHolder>> checkForTransactionInThreadLocalStorage() {
return TransactionMarkerOwner.get().flatMap(markerOpt -> {
Optional<CouchbaseResourceHolder> out = markerOpt
.flatMap(marker -> Optional.of(new CouchbaseResourceHolder(marker.context())));
return Mono.just(out);
});
}
public static Mono<Void> verifyNotInTransaction(String methodName) {
return checkForTransactionInThreadLocalStorage().flatMap(s -> {
if (s.isPresent()) {
return Mono.error(new IllegalArgumentException(methodName + "can not be used inside a transaction"));
} else {
return Mono.empty();
}
});
}
public static RuntimeException retryTransactionOnCasMismatch(CoreTransactionAttemptContext ctx, long cas1,
long cas2) {
try {
ctx.logger().info(ctx.attemptId(), "Spring CAS mismatch %s != %s, retrying transaction", cas1, cas2);
TransactionOperationFailedException err = TransactionOperationFailedException.Builder.createError()
.retryTransaction().cause(new CasMismatchException(null)).build();
return ctx.operationFailed(err);
} catch (Throwable err) {
return new RuntimeException(err);
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.NonTransientDataAccessException;
import com.couchbase.client.core.service.ServiceType;
/**
* A {@link NonTransientDataAccessException} that denotes that a particular feature is expected on the server side but
* is not available.
*/
public class UnsupportedCouchbaseFeatureException extends InvalidDataAccessApiUsageException {
private final ServiceType feature;
public UnsupportedCouchbaseFeatureException(String msg, ServiceType feature) {
super(msg);
this.feature = feature;
}
public UnsupportedCouchbaseFeatureException(String msg, ServiceType feature, Throwable cause) {
super(msg, cause);
this.feature = feature;
}
/**
* @return the {@link ServiceType} that was missing (could be null if not a registered CouchbaseFeature, in which case
* see {@link #getMessage()}).
*/
public ServiceType getFeature() {
return feature;
}
}

View File

@@ -1,131 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import java.util.Collections;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mapping.model.EntityInstantiators;
/**
* An abstract {@link CouchbaseConverter} that provides the basics for the {@link MappingCouchbaseConverter}.
*
* @author Michael Nitschinger
* @author Mark Paluch
*/
public abstract class AbstractCouchbaseConverter implements CouchbaseConverter, InitializingBean {
/**
* Contains the conversion service.
*/
protected final GenericConversionService conversionService;
/**
* Contains the entity instantiators.
*/
protected EntityInstantiators instantiators = new EntityInstantiators();
/**
* Holds the custom conversions.
*/
protected CustomConversions conversions = new CouchbaseCustomConversions(Collections.emptyList());
/**
* Create a new converter and hand it over the {@link ConversionService}
*
* @param conversionService the conversion service to use.
*/
protected AbstractCouchbaseConverter(final GenericConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* Return the conversion service.
*
* @return the conversion service.
*/
@Override
public ConversionService getConversionService() {
return conversionService;
}
/**
* Set the custom conversions. Note that updating conversions requires a subsequent call to register them with the
* conversionService: conversions.registerConvertersIn(conversionService)
*
* @param conversions the conversions.
*/
public void setCustomConversions(final CustomConversions conversions) {
this.conversions = conversions;
}
/**
* Set the entity instantiators.
*
* @param instantiators the instantiators.
*/
public void setInstantiators(final EntityInstantiators instantiators) {
this.instantiators = instantiators;
}
/**
* Do nothing after the properties set on the bean.
*/
@Override
public void afterPropertiesSet() {
conversions.registerConvertersIn(conversionService);
}
@Override
public Object convertForWriteIfNeeded(Object value) {
if (value == null) {
return null;
}
return this.conversions.getCustomWriteTarget(value.getClass()) //
.map(it -> (Object) this.conversionService.convert(value, it)) //
.orElseGet(() -> Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value);
}
/* TODO needed later
@Override
public Object convertToCouchbaseType(Object value, TypeInformation<?> typeInformation) {
if (value == null) {
return null;
}
return this.conversions.getCustomWriteTarget(value.getClass()) //
.map(it -> (Object) this.conversionService.convert(value, it)) //
.orElseGet(() -> Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value);
}
@Override
public Object convertToCouchbaseType(String source) {
return source;
}
*/
@Override
public Class<?> getWriteClassFor(Class<?> clazz) {
return this.conversions.getCustomWriteTarget(clazz).orElse(clazz);
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.data.couchbase.core.mapping.CouchbaseSimpleTypes;
import org.springframework.util.Assert;
/**
* Conversion registration information.
*
* @author Oliver Gierke
* @author Michael Nitschinger
* @author Mark Paluch
*/
class ConverterRegistration {
private final ConvertiblePair convertiblePair;
private final boolean reading;
private final boolean writing;
/**
* Creates a new {@link ConverterRegistration}.
*
* @param convertiblePair must not be {@literal null}.
* @param isReading whether to force to consider the converter for reading.
* @param isWriting whether to force to consider the converter for reading.
*/
public ConverterRegistration(ConvertiblePair convertiblePair, boolean isReading, boolean isWriting) {
Assert.notNull(convertiblePair, "ConvertiblePair must not be null!");
this.convertiblePair = convertiblePair;
reading = isReading;
writing = isWriting;
}
/**
* Creates a new {@link ConverterRegistration} from the given source and target type and read/write flags.
*
* @param source the source type to be converted from, must not be {@literal null}.
* @param target the target type to be converted to, must not be {@literal null}.
* @param isReading whether to force to consider the converter for reading.
* @param isWriting whether to force to consider the converter for writing.
*/
public ConverterRegistration(Class<?> source, Class<?> target, boolean isReading, boolean isWriting) {
this(new ConvertiblePair(source, target), isReading, isWriting);
}
/**
* Returns whether the given type is a type that Couchbase can handle basically.
*
* @param type
* @return
*/
private static boolean isCouchbaseBasicType(Class<?> type) {
return CouchbaseSimpleTypes.JSON_TYPES.isSimpleType(type);
}
/**
* Returns whether the converter shall be used for writing.
*
* @return
*/
public boolean isWriting() {
return writing == true || (!reading && isSimpleTargetType());
}
/**
* Returns whether the converter shall be used for reading.
*
* @return
*/
public boolean isReading() {
return reading == true || (!writing && isSimpleSourceType());
}
/**
* Returns the actual conversion pair.
*
* @return
*/
public ConvertiblePair getConvertiblePair() {
return convertiblePair;
}
/**
* Returns whether the source type is a Couchbase simple one.
*
* @return
*/
public boolean isSimpleSourceType() {
return isCouchbaseBasicType(convertiblePair.getSourceType());
}
/**
* Returns whether the target type is a Couchbase simple one.
*
* @return
*/
public boolean isSimpleTargetType() {
return isCouchbaseBasicType(convertiblePair.getTargetType());
}
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import org.springframework.data.convert.EntityConverter;
import org.springframework.data.convert.EntityReader;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.mapping.Alias;
import org.springframework.data.util.TypeInformation;
/**
* Marker interface for the converter, identifying the types to and from that can be converted.
*
* @author Michael Nitschinger
* @author Simon Baslé
* @author Michael Reiche
*/
public interface CouchbaseConverter
extends EntityConverter<CouchbasePersistentEntity<?>, CouchbasePersistentProperty, Object, CouchbaseDocument>,
CouchbaseWriter<Object, CouchbaseDocument>, EntityReader<Object, CouchbaseDocument> {
/**
* Convert the value if necessary to the class that would actually be stored, or leave it as is if no conversion
* needed.
*
* @param value the value to be converted to the class that would actually be stored.
* @return the converted value (or the same value if no conversion necessary).
*/
Object convertForWriteIfNeeded(Object value);
/**
* Return the Class that would actually be stored for a given Class.
*
* @param clazz the source class.
* @return the target class that would actually be stored.
* @see #convertForWriteIfNeeded(Object)
*/
Class<?> getWriteClassFor(Class<?> clazz);
/**
* @return the name of the field that will hold type information.
*/
String getTypeKey();
/**
* @return the alias value for the type
*/
Alias getTypeAlias(TypeInformation<?> info);
// TODO needed later
// CouchbaseTypeMapper getMapper();
// Object convertToCouchbaseType(Object source, TypeInformation<?> typeInformation);
//
// Object convertToCouchbaseType(String source);
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.data.mapping.model.SimpleTypeHolder;
/**
* Value object to capture custom conversion.
* <p>
* Types that can be mapped directly onto JSON are considered simple ones, because they neither need deeper inspection
* nor nested conversion.
*
* @author Michael Nitschinger
* @author Oliver Gierke
* @author Mark Paluch
* @author Subhashni Balakrishnan
* @see org.springframework.data.convert.CustomConversions
* @see SimpleTypeHolder
* @since 2.0
*/
public class CouchbaseCustomConversions extends org.springframework.data.convert.CustomConversions {
private static final StoreConversions STORE_CONVERSIONS;
private static final List<Object> STORE_CONVERTERS;
static {
List<Object> converters = new ArrayList<>();
converters.addAll(DateConverters.getConvertersToRegister());
converters.addAll(CouchbaseJsr310Converters.getConvertersToRegister());
converters.addAll(OtherConverters.getConvertersToRegister());
STORE_CONVERTERS = Collections.unmodifiableList(converters);
STORE_CONVERSIONS = StoreConversions.of(SimpleTypeHolder.DEFAULT, STORE_CONVERTERS);
}
/**
* Create a new instance with a given list of converters.
*
* @param converters the list of custom converters.
*/
public CouchbaseCustomConversions(final List<?> converters) {
super(STORE_CONVERSIONS, converters);
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import java.util.Map;
import org.springframework.context.expression.MapAccessor;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.TypedValue;
/**
* A property accessor for document properties.
*
* @author Michael Nitschinger
*/
public class CouchbaseDocumentPropertyAccessor extends MapAccessor {
/**
* Contains the static instance of thi accessor.
*/
static final MapAccessor INSTANCE = new CouchbaseDocumentPropertyAccessor();
/**
* Returns the target classes of the properties.
*
* @return
*/
@Override
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { CouchbaseDocument.class };
}
/**
* It can always read from those properties.
*
* @param context the evaluation context.
* @param target the target object.
* @param name the name of the property.
* @return always true.
*/
@Override
public boolean canRead(final EvaluationContext context, final Object target, final String name) {
return true;
}
/**
* Read the value from the property.
*
* @param context the evaluation context.
* @param target the target object.
* @param name the name of the property.
* @return the typed value of the content to be read.
*/
@Override
public TypedValue read(final EvaluationContext context, final Object target, final String name) {
Map<String, Object> source = (Map<String, Object>) target;
Object value = source.get(name);
return value == null ? TypedValue.NULL : new TypedValue(value);
}
}

View File

@@ -1,272 +0,0 @@
/*
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import static java.time.Instant.*;
import static java.time.LocalDateTime.*;
import static java.time.ZoneId.*;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
/**
* Helper class to register JSR-310 specific {@link Converter} implementations.
*
* @author Oliver Gierke
* @author Barak Schoster
* @author Christoph Strobl
* @author Subhashni Balakrishnan
*/
public final class CouchbaseJsr310Converters {
private CouchbaseJsr310Converters() {
}
/**
* Returns the converters to be registered
*
* @return
*/
public static Collection<Converter<?, ?>> getConvertersToRegister() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(NumberToLocalDateTimeConverter.INSTANCE);
converters.add(LocalDateTimeToLongConverter.INSTANCE);
converters.add(NumberToLocalDateConverter.INSTANCE);
converters.add(LocalDateToLongConverter.INSTANCE);
converters.add(NumberToLocalTimeConverter.INSTANCE);
converters.add(LocalTimeToLongConverter.INSTANCE);
converters.add(NumberToInstantConverter.INSTANCE);
converters.add(InstantToLongConverter.INSTANCE);
converters.add(ZoneIdToStringConverter.INSTANCE);
converters.add(StringToZoneIdConverter.INSTANCE);
converters.add(DurationToStringConverter.INSTANCE);
converters.add(StringToDurationConverter.INSTANCE);
converters.add(PeriodToStringConverter.INSTANCE);
converters.add(StringToPeriodConverter.INSTANCE);
converters.add(ZonedDateTimeToLongConverter.INSTANCE);
converters.add(NumberToZonedDateTimeConverter.INSTANCE);
return converters;
}
@ReadingConverter
public enum NumberToLocalDateTimeConverter implements Converter<Number, LocalDateTime> {
INSTANCE;
@Override
public LocalDateTime convert(Number source) {
return source == null ? null
: ofInstant(DateConverters.SerializedObjectToDateConverter.INSTANCE.convert(source).toInstant(),
systemDefault());
}
}
@WritingConverter
public enum LocalDateTimeToLongConverter implements Converter<LocalDateTime, Long> {
INSTANCE;
@Override
public Long convert(LocalDateTime source) {
return source == null ? null
: DateConverters.DateToLongConverter.INSTANCE.convert(Date.from(source.atZone(systemDefault()).toInstant()));
}
}
@ReadingConverter
public enum NumberToZonedDateTimeConverter implements Converter<Number, ZonedDateTime> {
INSTANCE;
@Override
public ZonedDateTime convert(Number source) {
return source == null ? null
: ZonedDateTime.ofInstant(DateConverters.SerializedObjectToDateConverter.INSTANCE.convert(source).toInstant(),
systemDefault());
}
}
@WritingConverter
public enum ZonedDateTimeToLongConverter implements Converter<ZonedDateTime, Long> {
INSTANCE;
@Override
public Long convert(ZonedDateTime source) {
return source == null ? null
: DateConverters.DateToLongConverter.INSTANCE.convert(Date.from(source.toInstant()));
}
}
@ReadingConverter
public enum NumberToLocalDateConverter implements Converter<Number, LocalDate> {
INSTANCE;
@Override
public LocalDate convert(Number source) {
return source == null ? null
: ofInstant(ofEpochMilli(DateConverters.SerializedObjectToDateConverter.INSTANCE.convert(source).getTime()),
systemDefault()).toLocalDate();
}
}
@WritingConverter
public enum LocalDateToLongConverter implements Converter<LocalDate, Long> {
INSTANCE;
@Override
public Long convert(LocalDate source) {
return source == null ? null
: DateConverters.DateToLongConverter.INSTANCE
.convert(Date.from(source.atStartOfDay(systemDefault()).toInstant()));
}
}
@ReadingConverter
public enum NumberToLocalTimeConverter implements Converter<Number, LocalTime> {
INSTANCE;
@Override
public LocalTime convert(Number source) {
return source == null ? null
: ofInstant(ofEpochMilli(DateConverters.SerializedObjectToDateConverter.INSTANCE.convert(source).getTime()),
systemDefault()).toLocalTime();
}
}
@WritingConverter
public enum LocalTimeToLongConverter implements Converter<LocalTime, Long> {
INSTANCE;
@Override
public Long convert(LocalTime source) {
return source == null ? null
: DateConverters.DateToLongConverter.INSTANCE
.convert(Date.from(source.atDate(LocalDate.now()).atZone(systemDefault()).toInstant()));
}
}
@ReadingConverter
public enum NumberToInstantConverter implements Converter<Number, Instant> {
INSTANCE;
@Override
public Instant convert(Number source) {
return source == null ? null
: DateConverters.SerializedObjectToDateConverter.INSTANCE.convert(source).toInstant();
}
}
@WritingConverter
public enum InstantToLongConverter implements Converter<Instant, Long> {
INSTANCE;
@Override
public Long convert(Instant source) {
return source == null ? null
: DateConverters.DateToLongConverter.INSTANCE.convert(Date.from(source.atZone(systemDefault()).toInstant()));
}
}
@WritingConverter
public enum ZoneIdToStringConverter implements Converter<ZoneId, String> {
INSTANCE;
@Override
public String convert(ZoneId source) {
return source.toString();
}
}
@ReadingConverter
public enum StringToZoneIdConverter implements Converter<String, ZoneId> {
INSTANCE;
@Override
public ZoneId convert(String source) {
return ZoneId.of(source);
}
}
@WritingConverter
public enum DurationToStringConverter implements Converter<Duration, String> {
INSTANCE;
@Override
public String convert(Duration duration) {
return duration.toString();
}
}
@ReadingConverter
public enum StringToDurationConverter implements Converter<String, Duration> {
INSTANCE;
@Override
public Duration convert(String s) {
return Duration.parse(s);
}
}
@WritingConverter
public enum PeriodToStringConverter implements Converter<Period, String> {
INSTANCE;
@Override
public String convert(Period period) {
return period.toString();
}
}
@ReadingConverter
public enum StringToPeriodConverter implements Converter<String, Period> {
INSTANCE;
@Override
public Period convert(String s) {
return Period.parse(s);
}
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import org.springframework.data.convert.TypeMapper;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.mapping.Alias;
import org.springframework.data.util.TypeInformation;
/**
* Marker interface for the TypeMapper.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
public interface CouchbaseTypeMapper extends TypeMapper<CouchbaseDocument> {
String getTypeKey();
Alias getTypeAlias(TypeInformation<?> info);
}

View File

@@ -1,26 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import org.springframework.data.convert.EntityWriter;
/**
* Marker interface for the Couchbase {@link EntityWriter}.
*
* @author Michael Nitschinger
*/
public interface CouchbaseWriter<T, ConvertedCouchbaseDocument> extends EntityWriter<T, ConvertedCouchbaseDocument> {}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import java.util.List;
/**
* Value object to capture custom conversion.
* <p>
* Types that can be mapped directly onto JSON are considered simple ones, because they neither need deeper inspection
* nor nested conversion.
*
* @author Michael Nitschinger
* @author Oliver Gierke
* @author Mark Paluch
* @author Subhashni Balakrishnan
* @deprecated since 2.0, use {@link CouchbaseCustomConversions}.
*/
@Deprecated
public class CustomConversions extends CouchbaseCustomConversions {
/**
* Create a new instance with a given list of conversers.
*
* @param converters the list of custom converters.
*/
public CustomConversions(final List<?> converters) {
super(converters);
}
}

View File

@@ -1,211 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import static java.time.ZoneId.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.util.ClassUtils;
/**
* Out of the box conversions for java dates and calendars.
*
* @author Michael Nitschinger
* @author Subhashni Balakrishnan
* @author Mark Paluch
*/
public final class DateConverters {
private static final boolean JODA_TIME_IS_PRESENT = ClassUtils.isPresent("org.joda.time.LocalDate", null);
private DateConverters() {}
/**
* Returns all converters by this class that can be registered.
*
* @return the list of converters to register.
*/
public static Collection<Converter<?, ?>> getConvertersToRegister() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
boolean useISOStringConverterForDate = Boolean
.parseBoolean(System.getProperty("org.springframework.data.couchbase.useISOStringConverterForDate", "false"));
if (useISOStringConverterForDate) {
converters.add(DateToStringConverter.INSTANCE);
} else {
converters.add(DateToLongConverter.INSTANCE);
}
converters.add(SerializedObjectToDateConverter.INSTANCE);
converters.add(CalendarToLongConverter.INSTANCE);
converters.add(NumberToCalendarConverter.INSTANCE);
if (JODA_TIME_IS_PRESENT) {
converters.add(LocalDateToLongConverter.INSTANCE);
converters.add(LocalDateTimeToLongConverter.INSTANCE);
converters.add(DateTimeToLongConverter.INSTANCE);
converters.add(NumberToLocalDateConverter.INSTANCE);
converters.add(NumberToLocalDateTimeConverter.INSTANCE);
converters.add(NumberToDateTimeConverter.INSTANCE);
}
return converters;
}
@WritingConverter
public enum DateToStringConverter implements Converter<Date, String> {
INSTANCE;
@Override
public String convert(Date source) {
return source == null ? null : source.toInstant().toString();
}
}
@ReadingConverter
public enum SerializedObjectToDateConverter implements Converter<Object, Date> {
INSTANCE;
@Override
public Date convert(Object source) {
if (source == null) {
return null;
}
if (source instanceof Number) {
Date date = new Date();
date.setTime(((Number) source).longValue());
return date;
} else if (source instanceof String) {
return Date.from(Instant.parse((String) source).atZone(systemDefault()).toInstant());
} else {
// Unsupported serialized object
return null;
}
}
}
@WritingConverter
public enum DateToLongConverter implements Converter<Date, Long> {
INSTANCE;
@Override
public Long convert(Date source) {
return source == null ? null : source.getTime();
}
}
@WritingConverter
public enum CalendarToLongConverter implements Converter<Calendar, Long> {
INSTANCE;
@Override
public Long convert(Calendar source) {
return source == null ? null : source.getTimeInMillis() / 1000;
}
}
@ReadingConverter
public enum NumberToCalendarConverter implements Converter<Number, Calendar> {
INSTANCE;
@Override
public Calendar convert(Number source) {
if (source == null) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(source.longValue() * 1000);
return calendar;
}
}
@WritingConverter
public enum LocalDateToLongConverter implements Converter<LocalDate, Long> {
INSTANCE;
@Override
public Long convert(LocalDate source) {
return source == null ? null : source.toDate().getTime();
}
}
@WritingConverter
public enum LocalDateTimeToLongConverter implements Converter<LocalDateTime, Long> {
INSTANCE;
@Override
public Long convert(LocalDateTime source) {
return source == null ? null : source.toDate().getTime();
}
}
@WritingConverter
public enum DateTimeToLongConverter implements Converter<DateTime, Long> {
INSTANCE;
@Override
public Long convert(DateTime source) {
return source == null ? null : source.toDate().getTime();
}
}
@ReadingConverter
public enum NumberToLocalDateConverter implements Converter<Number, LocalDate> {
INSTANCE;
@Override
public LocalDate convert(Number source) {
return source == null ? null : new LocalDate(source.longValue());
}
}
@ReadingConverter
public enum NumberToLocalDateTimeConverter implements Converter<Number, LocalDateTime> {
INSTANCE;
@Override
public LocalDateTime convert(Number source) {
return source == null ? null : new LocalDateTime(source.longValue());
}
}
@ReadingConverter
public enum NumberToDateTimeConverter implements Converter<Number, DateTime> {
INSTANCE;
@Override
public DateTime convert(Number source) {
return source == null ? null : new DateTime(source.longValue());
}
}
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import java.util.Collections;
import org.springframework.data.convert.DefaultTypeMapper;
import org.springframework.data.convert.TypeAliasAccessor;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.mapping.Alias;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.TypeInformation;
/**
* The Couchbase Type Mapper.
*
* @author Michael Nitschinger
* @author Mark Paluch
* @author Michael Reiche
*/
public class DefaultCouchbaseTypeMapper extends DefaultTypeMapper<CouchbaseDocument> implements CouchbaseTypeMapper {
/**
* The type key to use if a complex type was identified.
*/
public static final String DEFAULT_TYPE_KEY = "_class";
private final String typeKey;
/**
* Create a new type mapper with the type key.
*
* @param typeKey the typeKey to use.
*/
public DefaultCouchbaseTypeMapper(final String typeKey) {
super(new CouchbaseDocumentTypeAliasAccessor(typeKey), (MappingContext) null,
Collections.singletonList(new TypeAwareTypeInformationMapper()));
this.typeKey = typeKey;
}
@Override
public String getTypeKey() {
return this.typeKey;
}
public static final class CouchbaseDocumentTypeAliasAccessor implements TypeAliasAccessor<CouchbaseDocument> {
private final String typeKey;
public CouchbaseDocumentTypeAliasAccessor(final String typeKey) {
this.typeKey = typeKey;
}
@Override
public Alias readAliasFrom(final CouchbaseDocument source) {
return Alias.ofNullable(source.get(typeKey));
}
@Override
public void writeTypeTo(final CouchbaseDocument sink, final Object alias) {
if (typeKey != null) {
sink.put(typeKey, alias);
}
}
}
@Override
public Alias getTypeAlias(TypeInformation<?> info) {
return getAliasFor(info);
}
}

View File

@@ -1,996 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.UNIQUE;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.USE_ATTRIBUTES;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.UUID;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.annotation.Transient;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseList;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.event.AfterConvertCallback;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.IdAttribute;
import org.springframework.data.couchbase.core.mapping.id.IdPrefix;
import org.springframework.data.couchbase.core.mapping.id.IdSuffix;
import org.springframework.data.couchbase.core.query.N1qlJoin;
import org.springframework.data.mapping.Alias;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.Parameter;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
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.CollectionUtils;
/**
* A mapping converter for Couchbase. The converter is responsible for reading from and writing to entities and
* converting it into a consumable database representation.
*
* @author Michael Nitschinger
* @author Oliver Gierke
* @author Geoffrey Mina
* @author Mark Paluch
* @author Michael Reiche
*/
public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implements ApplicationContextAware {
/**
* The default "type key", the name of the field that will hold type information.
*
* @see #TYPEKEY_SYNCGATEWAY_COMPATIBLE
*/
public static final String TYPEKEY_DEFAULT = DefaultCouchbaseTypeMapper.DEFAULT_TYPE_KEY;
/**
* A "type key" (the name of the field that will hold type information) that is compatible with Sync Gateway (which
* doesn't allows underscores).
*/
public static final String TYPEKEY_SYNCGATEWAY_COMPATIBLE = "javaClass";
/**
* The generic mapping context.
*/
protected final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
/**
* Spring Expression Language context.
*/
private final SpELContext spELContext;
/**
* The overall application context.
*/
protected ApplicationContext applicationContext;
/**
* The Couchbase specific type mapper in use.
*/
protected CouchbaseTypeMapper typeMapper;
/**
* Callbacks for Audit Mechanism
*/
private @Nullable EntityCallbacks entityCallbacks;
public MappingCouchbaseConverter() {
this(new CouchbaseMappingContext(), null);
}
/**
* Create a new {@link MappingCouchbaseConverter}.
*
* @param mappingContext the mapping context to use.
*/
public MappingCouchbaseConverter(
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext) {
this(mappingContext, null);
}
/**
* Create a new {@link MappingCouchbaseConverter} that will store class name for complex types in the <i>typeKey</i>
* attribute.
*
* @param mappingContext the mapping context to use.
* @param typeKey the attribute name to use to store complex types class name.
*/
public MappingCouchbaseConverter(
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
final String typeKey) {
super(new DefaultConversionService());
this.mappingContext = mappingContext;
// this is how the MappingCouchbaseConverter gets the custom conversions.
// the conversions Service gets them in afterPropertiesSet()
CustomConversions customConversions = new CouchbaseCustomConversions(Collections.emptyList());
this.setCustomConversions(customConversions);
// if the mappingContext does not have the SimpleTypes, it will not know that they have converters, then it will
// try to access the fields of the type and (maybe) fail with InaccessibleObjectException
((CouchbaseMappingContext) mappingContext).setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
typeMapper = new DefaultCouchbaseTypeMapper(typeKey != null ? typeKey : TYPEKEY_DEFAULT);
spELContext = new SpELContext(CouchbaseDocumentPropertyAccessor.INSTANCE);
}
/**
* Returns a collection from the given source object.
*
* @param source the source object.
* @return the target collection.
*/
private static Collection<?> asCollection(final Object source) {
if (source instanceof Collection) {
return (Collection<?>) source;
}
return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source);
}
/**
* Check if one class is a subtype of the other.
*
* @param left the first class.
* @param right the second class.
* @return true if it is a subtype, false otherwise.
*/
private static boolean isSubtype(final Class<?> left, final Class<?> right) {
return left.isAssignableFrom(right) && !left.equals(right);
}
@Override
public MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> getMappingContext() {
return mappingContext;
}
@Override
public String getTypeKey() {
return typeMapper.getTypeKey();
}
@Override
public Alias getTypeAlias(TypeInformation<?> info) {
return typeMapper.getTypeAlias(info);
}
@Override
public <R> R read(final Class<R> clazz, final CouchbaseDocument source) {
return read(ClassTypeInformation.from(clazz), source, null);
}
/**
* Read an incoming {@link CouchbaseDocument} into the target entity.
*
* @param type the type information of the target entity.
* @param source the document to convert.
* @param <R> the entity type.
* @return the converted entity.
*/
protected <R> R read(final TypeInformation<R> type, final CouchbaseDocument source) {
return read(type, source, null);
}
/**
* Read an incoming {@link CouchbaseDocument} into the target entity.
*
* @param type the type information of the target entity.
* @param source the document to convert.
* @param parent an optional parent object.
* @param <R> the entity type.
* @return the converted entity.
*/
@SuppressWarnings("unchecked")
protected <R> R read(final TypeInformation<R> type, final CouchbaseDocument source, final Object parent) {
if (source == null) {
return null;
}
TypeInformation<? extends R> typeToUse = typeMapper.readType(source, type);
Class<? extends R> rawType = typeToUse.getType();
if (conversions.hasCustomReadTarget(source.getClass(), rawType)) {
return conversionService.convert(source, rawType);
}
if (typeToUse.isMap()) {
return (R) readMap(typeToUse, source, parent);
}
CouchbasePersistentEntity<R> entity = (CouchbasePersistentEntity<R>) mappingContext
.getRequiredPersistentEntity(typeToUse);
return read(entity, source, parent);
}
private boolean isIdConstructionProperty(final CouchbasePersistentProperty property) {
return property.isAnnotationPresent(IdPrefix.class) || property.isAnnotationPresent(IdSuffix.class);
}
/**
* Read an incoming {@link CouchbaseDocument} into the target entity.
*
* @param entity the target entity.
* @param source the document to convert.
* @param parent an optional parent object.
* @param <R> the entity type.
* @return the converted entity.
*/
protected <R> R read(final CouchbasePersistentEntity<R> entity, final CouchbaseDocument source, final Object parent) {
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(source, spELContext);
ParameterValueProvider<CouchbasePersistentProperty> provider = getParameterProvider(entity, source, evaluator,
parent);
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
final R instance = instantiator.createInstance(entity, provider);
final ConvertingPropertyAccessor accessor = getPropertyAccessor(instance);
entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
@Override
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
if (!doesPropertyExistInSource(prop) || entity.isConstructorArgument(prop) || isIdConstructionProperty(prop)
|| prop.isAnnotationPresent(N1qlJoin.class)) {
return;
}
Object obj = prop == entity.getIdProperty() && parent == null ? source.getId()
: getValueInternal(prop, source, instance, entity);
accessor.setProperty(prop, obj);
}
private boolean doesPropertyExistInSource(final CouchbasePersistentProperty property) {
return property.isIdProperty() || source.containsKey(property.getFieldName());
}
private boolean isIdConstructionProperty(final CouchbasePersistentProperty property) {
return property.isAnnotationPresent(IdPrefix.class) || property.isAnnotationPresent(IdSuffix.class);
}
});
entity.doWithAssociations((AssociationHandler<CouchbasePersistentProperty>) association -> {
CouchbasePersistentProperty inverseProp = association.getInverse();
Object obj = getValueInternal(inverseProp, source, instance, entity);
accessor.setProperty(inverseProp, obj);
});
return instance;
}
/**
* Loads the property value through the value provider.
*
* @param property the source property.
* @param source the source document.
* @param parent the optional parent.
* @return the actual property value.
*/
protected Object getValueInternal(final CouchbasePersistentProperty property, final CouchbaseDocument source,
final Object parent, PersistentEntity entity) {
return new CouchbasePropertyValueProvider(source, spELContext, parent, entity).getPropertyValue(property);
}
/**
* Creates a new parameter provider.
*
* @param entity the persistent entity.
* @param source the source document.
* @param evaluator the SPEL expression evaluator.
* @param parent the optional parent.
* @return a new parameter value provider.
*/
private ParameterValueProvider<CouchbasePersistentProperty> getParameterProvider(
final CouchbasePersistentEntity<?> entity, final CouchbaseDocument source,
final DefaultSpELExpressionEvaluator evaluator, final Object parent) {
CouchbasePropertyValueProvider provider = new CouchbasePropertyValueProvider(source, evaluator, parent, entity);
PersistentEntityParameterValueProvider<CouchbasePersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<>(
entity, provider, parent);
return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider,
parent);
}
/**
* Recursively parses the a map from the source document.
*
* @param type the type information for the document.
* @param source the source document.
* @param parent the optional parent.
* @return the recursively parsed map.
*/
@SuppressWarnings("unchecked")
protected Map<Object, Object> readMap(final TypeInformation<?> type, final CouchbaseDocument source,
final Object parent) {
Assert.notNull(source, "CouchbaseDocument must not be null!");
Class<?> mapType = typeMapper.readType(source, type).getType();
Map<Object, Object> map = CollectionFactory.createMap(mapType, source.export().keySet().size());
Map<String, Object> sourceMap = source.getContent();
for (Map.Entry<String, Object> entry : sourceMap.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
TypeInformation<?> keyTypeInformation = type.getComponentType();
if (keyTypeInformation != null) {
Class<?> keyType = keyTypeInformation.getType();
key = conversionService.convert(key, keyType);
}
TypeInformation<?> valueType = type.getMapValueType();
if (value instanceof CouchbaseDocument) {
map.put(key, read(valueType, (CouchbaseDocument) value, parent));
} else if (value instanceof CouchbaseList) {
map.put(key, readCollection(valueType, (CouchbaseList) value, parent));
} else {
Class<?> valueClass = valueType == null ? null : valueType.getType();
map.put(key, getPotentiallyConvertedSimpleRead(value, valueClass));
}
}
return map;
}
/**
* Potentially convert simple values like ENUMs.
*
* @param value the value to convert.
* @param target the target object.
* @return the potentially converted object.
*/
@SuppressWarnings("unchecked")
private Object getPotentiallyConvertedSimpleRead(final Object value, final Class<?> target) {
if (value == null || target == null) {
return value;
}
if (conversions.hasCustomReadTarget(value.getClass(), target)) {
return conversionService.convert(value, target);
}
if (Enum.class.isAssignableFrom(target)) {
return Enum.valueOf((Class<Enum>) target, value.toString());
}
if (Class.class.isAssignableFrom(target)) {
try {
return Class.forName(value.toString());
} catch (ClassNotFoundException e) {
throw new MappingException("Unable to create class from " + value.toString());
}
}
return target.isAssignableFrom(value.getClass()) ? value : conversionService.convert(value, target);
}
@Override
public void write(final Object source, final CouchbaseDocument target) {
if (source == null) {
return;
}
boolean isCustom = conversions.getCustomWriteTarget(source.getClass(), CouchbaseDocument.class).isPresent();
TypeInformation<?> type = ClassTypeInformation.from(source.getClass());
if (!isCustom) {
typeMapper.writeType(type, target);
}
writeInternal(source, target, type, true);
if (target.getId() == null) {
throw new MappingException("An ID property is needed, but not found/could not be generated on this entity.");
}
}
/**
* Convert a source object into a {@link CouchbaseDocument} target.
*
* @param source the source object.
* @param target the target document.
* @param typeHint the type information for the source.
*/
@SuppressWarnings("unchecked")
protected void writeInternal(final Object source, CouchbaseDocument target, final TypeInformation<?> typeHint,
boolean withId) {
if (source == null) {
return;
}
Optional<Class<?>> customTarget = conversions.getCustomWriteTarget(source.getClass(), CouchbaseDocument.class);
if (customTarget.isPresent()) {
copyCouchbaseDocument(conversionService.convert(source, CouchbaseDocument.class), target);
return;
}
if (Map.class.isAssignableFrom(source.getClass())) {
writeMapInternal((Map<Object, Object>) source, target, ClassTypeInformation.MAP);
return;
}
if (Collection.class.isAssignableFrom(source.getClass())) {
throw new IllegalArgumentException("Root Document must be either CouchbaseDocument or Map.");
}
CouchbasePersistentEntity<?> entity = mappingContext.getPersistentEntity(source.getClass());
writeInternal(source, target, entity, withId);
addCustomTypeKeyIfNecessary(typeHint, source, target);
}
/**
* Helper method to copy the internals from a source document into a target document.
*
* @param source the source document.
* @param target the target document.
*/
protected void copyCouchbaseDocument(final CouchbaseDocument source, final CouchbaseDocument target) {
for (Map.Entry<String, Object> entry : source.export().entrySet()) {
target.put(entry.getKey(), entry.getValue());
}
target.setId(source.getId());
target.setExpiration(source.getExpiration());
}
private String convertToString(Object propertyObj) {
if (propertyObj instanceof String) {
return (String) propertyObj;
} else if (propertyObj instanceof Number) {
return new StringBuffer().append(propertyObj).toString();
} else {
return propertyObj.toString();
}
}
/**
* Internal helper method to write the source object into the target document.
*
* @param source the source object.
* @param target the target document.
* @param entity the persistent entity to convert from.
* @param withId one of the top-level properties is the id for the document
*/
protected void writeInternal(final Object source, final CouchbaseDocument target,
final CouchbasePersistentEntity<?> entity, boolean withId) {
if (source == null) {
return;
}
if (entity == null) {
throw new MappingException("No mapping metadata found for entity of type " + source.getClass().getName());
}
final ConvertingPropertyAccessor<Object> accessor = getPropertyAccessor(source);
final CouchbasePersistentProperty idProperty = withId ? entity.getIdProperty() : null;
final CouchbasePersistentProperty versionProperty = entity.getVersionProperty();
GeneratedValue generatedValueInfo = null;
final TreeMap<Integer, String> prefixes = new TreeMap<>();
final TreeMap<Integer, String> suffixes = new TreeMap<>();
final TreeMap<Integer, String> idAttributes = new TreeMap<>();
target.setExpiration((int) (entity.getExpiryDuration().getSeconds()));
writeToTargetDocument(target, entity, accessor, idProperty, versionProperty, prefixes, suffixes, idAttributes);
if (idProperty != null && target.getId() == null) {
String id = accessor.getProperty(idProperty, String.class);
if (idProperty.isAnnotationPresent(GeneratedValue.class) && (id == null || id.equals(""))) {
generatedValueInfo = idProperty.findAnnotation(GeneratedValue.class);
String generatedId = generateId(generatedValueInfo, prefixes, suffixes, idAttributes);
target.setId(generatedId);
// this is not effective if id is Immutable, and accessor.setProperty() returns a new object in getBean()
accessor.setProperty(idProperty, generatedId);
} else {
target.setId(id);
}
}
entity.doWithAssociations(new AssociationHandler<CouchbasePersistentProperty>() {
@Override
public void doWithAssociation(final Association<CouchbasePersistentProperty> association) {
CouchbasePersistentProperty inverseProp = association.getInverse();
Class<?> type = inverseProp.getType();
Object propertyObj = accessor.getProperty(inverseProp, type);
if (null != propertyObj) {
writePropertyInternal(propertyObj, target, inverseProp, false);
}
}
});
}
private void writeToTargetDocument(final CouchbaseDocument target, final CouchbasePersistentEntity<?> entity,
final ConvertingPropertyAccessor<Object> accessor, final CouchbasePersistentProperty idProperty,
final CouchbasePersistentProperty versionProperty, final TreeMap<Integer, String> prefixes,
final TreeMap<Integer, String> suffixes, final TreeMap<Integer, String> idAttributes) {
entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
@Override
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
if (prop.equals(idProperty) || (versionProperty != null && prop.equals(versionProperty))) {
return;
} else if (prop.isAnnotationPresent(N1qlJoin.class)) {
return;
}
Object propertyObj = accessor.getProperty(prop, prop.getType());
if (null != propertyObj) {
if (prop.isAnnotationPresent(IdPrefix.class)) {
IdPrefix prefix = prop.findAnnotation(IdPrefix.class);
int order = prefix.order();
prefixes.put(order, convertToString(propertyObj));
return;
}
if (prop.isAnnotationPresent(IdSuffix.class)) {
IdSuffix suffix = prop.findAnnotation(IdSuffix.class);
int order = suffix.order();
suffixes.put(order, convertToString(propertyObj));
return;
}
if (prop.isAnnotationPresent(IdAttribute.class)) {
IdAttribute idAttribute = prop.findAnnotation(IdAttribute.class);
int order = idAttribute.order();
idAttributes.put(order, convertToString(propertyObj));
}
if (prop.isAnnotationPresent(Transient.class)) {
return;
}
if (!conversions.isSimpleType(propertyObj.getClass())) {
writePropertyInternal(propertyObj, target, prop, false);
} else {
writeSimpleInternal(propertyObj, target, prop.getFieldName());
}
}
}
});
}
/**
* Helper method to write a property into the target document.
*
* @param source the source object.
* @param target the target document.
* @param prop the property information.
*/
@SuppressWarnings("unchecked")
private void writePropertyInternal(final Object source, final CouchbaseDocument target,
final CouchbasePersistentProperty prop, boolean withId) {
if (source == null) {
return;
}
String name = prop.getFieldName();
TypeInformation<?> valueType = ClassTypeInformation.from(source.getClass());
TypeInformation<?> type = prop.getTypeInformation();
if (valueType.isCollectionLike()) {
CouchbaseList collectionDoc = createCollection(asCollection(source), prop);
target.put(name, collectionDoc);
return;
}
if (valueType.isMap()) {
CouchbaseDocument mapDoc = createMap((Map<Object, Object>) source, prop);
target.put(name, mapDoc);
return;
}
if (valueType.getType().equals(java.util.Optional.class)) {
if (source == null)
return;
Optional<String> o = (Optional<String>) source;
if (o.isPresent()) {
writeSimpleInternal(o.get(), target, prop.getFieldName());
} else {
writeSimpleInternal(null, target, prop.getFieldName());
}
return;
}
Optional<Class<?>> basicTargetType = conversions.getCustomWriteTarget(source.getClass());
if (basicTargetType.isPresent()) {
basicTargetType.ifPresent(it -> {
target.put(name, conversionService.convert(source, it));
});
return;
}
CouchbaseDocument propertyDoc = new CouchbaseDocument();
addCustomTypeKeyIfNecessary(type, source, propertyDoc);
CouchbasePersistentEntity<?> entity = isSubtype(prop.getType(), source.getClass())
? mappingContext.getRequiredPersistentEntity(source.getClass())
: mappingContext.getRequiredPersistentEntity(type);
writeInternal(source, propertyDoc, entity, false);
target.put(name, propertyDoc);
}
/**
* Wrapper method to create the underlying map.
*
* @param map the source map.
* @param prop the persistent property.
* @return the written couchbase document.
*/
private CouchbaseDocument createMap(final Map<Object, Object> map, final CouchbasePersistentProperty prop) {
Assert.notNull(map, "Given map must not be null!");
Assert.notNull(prop, "PersistentProperty must not be null!");
return writeMapInternal(map, new CouchbaseDocument(), prop.getTypeInformation());
}
/**
* Helper method to write the map into the couchbase document.
*
* @param source the source object.
* @param target the target document.
* @param type the type information for the document.
* @return the written couchbase document.
*/
private CouchbaseDocument writeMapInternal(final Map<Object, Object> source, final CouchbaseDocument target,
final TypeInformation<?> type) {
for (Map.Entry<Object, Object> entry : source.entrySet()) {
Object key = entry.getKey();
Object val = entry.getValue();
if (conversions.isSimpleType(key.getClass())) {
String simpleKey = key.toString();
if (val == null || conversions.isSimpleType(val.getClass())) {
writeSimpleInternal(val, target, simpleKey);
} else if (val instanceof Collection || val.getClass().isArray()) {
target.put(simpleKey, writeCollectionInternal(asCollection(val),
new CouchbaseList(conversions.getSimpleTypeHolder()), type.getMapValueType()));
} else {
CouchbaseDocument embeddedDoc = new CouchbaseDocument();
TypeInformation<?> valueTypeInfo = type.isMap() ? type.getMapValueType() : ClassTypeInformation.OBJECT;
writeInternal(val, embeddedDoc, valueTypeInfo, false);
target.put(simpleKey, embeddedDoc);
}
} else {
throw new MappingException("Cannot use a complex object as a key value.");
}
}
return target;
}
/**
* Helper method to create the underlying collection/list.
*
* @param collection the collection to write.
* @param prop the property information.
* @return the created couchbase list.
*/
private CouchbaseList createCollection(final Collection<?> collection, final CouchbasePersistentProperty prop) {
return writeCollectionInternal(collection, new CouchbaseList(conversions.getSimpleTypeHolder()),
prop.getTypeInformation());
}
/**
* Helper method to write the internal collection.
*
* @param source the source object.
* @param target the target document.
* @param type the type information for the document.
* @return the created couchbase list.
*/
private CouchbaseList writeCollectionInternal(final Collection<?> source, final CouchbaseList target,
final TypeInformation<?> type) {
TypeInformation<?> componentType = type == null ? null : type.getComponentType();
for (Object element : source) {
Class<?> elementType = element == null ? null : element.getClass();
if (elementType == null || conversions.isSimpleType(elementType)) {
target.put(getPotentiallyConvertedSimpleWrite(element));
} else if (element instanceof Collection || elementType.isArray()) {
target.put(writeCollectionInternal(asCollection(element), new CouchbaseList(conversions.getSimpleTypeHolder()),
componentType));
} else {
CouchbaseDocument embeddedDoc = new CouchbaseDocument();
writeInternal(element, embeddedDoc, componentType, false);
target.put(embeddedDoc);
}
}
return target;
}
/**
* Read a collection from the source object.
*
* @param targetType the target type.
* @param source the list as source.
* @param parent the optional parent.
* @return the instantiated collection.
*/
@SuppressWarnings("unchecked")
private Object readCollection(final TypeInformation<?> targetType, final CouchbaseList source, final Object parent) {
Assert.notNull(targetType, "Target type must not be null!");
Class<?> collectionType = targetType.getType();
if (source.isEmpty()) {
return getPotentiallyConvertedSimpleRead(new HashSet<Object>(), collectionType);
}
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
: CollectionFactory.createCollection(collectionType, source.size(false));
TypeInformation<?> componentType = targetType.getComponentType();
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
for (int i = 0; i < source.size(false); i++) {
Object dbObjItem = source.get(i);
if (dbObjItem instanceof CouchbaseDocument) {
items.add(read(componentType, (CouchbaseDocument) dbObjItem, parent));
} else if (dbObjItem instanceof CouchbaseList) {
items.add(readCollection(componentType, (CouchbaseList) dbObjItem, parent));
} else {
items.add(getPotentiallyConvertedSimpleRead(dbObjItem, rawComponentType));
}
}
return getPotentiallyConvertedSimpleRead(items, targetType.getType());
}
/**
* Write the given source into the couchbase document target.
*
* @param source the source object.
* @param target the target document.
* @param key the key of the object.
*/
private void writeSimpleInternal(final Object source, final CouchbaseDocument target, final String key) {
target.put(key, getPotentiallyConvertedSimpleWrite(source));
}
public Object getPotentiallyConvertedSimpleWrite(final Object value) {
return convertForWriteIfNeeded(value);
}
/**
* Add a custom type key if needed.
*
* @param type the type information.
* @param source th the source object.
* @param target the target document.
*/
protected void addCustomTypeKeyIfNecessary(TypeInformation<?> type, Object source, CouchbaseDocument target) {
TypeInformation<?> actualType = type != null ? type.getActualType() : type;
Class<?> reference = actualType == null ? Object.class : actualType.getType();
boolean notTheSameClass = !source.getClass().equals(reference);
if (notTheSameClass) {
typeMapper.writeType(source.getClass(), target);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
if (entityCallbacks == null) {
setEntityCallbacks(EntityCallbacks.create(applicationContext));
}
}
/**
* COPIED Set the {@link EntityCallbacks} instance to use when invoking
* {@link org.springframework.data.mapping.callback.EntityCallback callbacks} like the {@link AfterConvertCallback}.
* Overrides potentially existing {@link EntityCallbacks}.
*
* @param entityCallbacks must not be {@literal null}.
* @throws IllegalArgumentException if the given instance is {@literal null}.
* @since 3.0
*/
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
Assert.notNull(entityCallbacks, "EntityCallbacks must not be null!");
this.entityCallbacks = entityCallbacks;
}
/**
* Helper method to read the value based on the value type.
*
* @param value the value to convert.
* @param type the type information.
* @param parent the optional parent.
* @param <R> the target type.
* @return the converted object.
*/
@SuppressWarnings("unchecked")
private <R> R readValue(Object value, TypeInformation<?> type, Object parent) {
Class<?> rawType = type.getType();
if (conversions.hasCustomReadTarget(value.getClass(), rawType)) {
return (R) conversionService.convert(value, rawType);
} else if (value instanceof CouchbaseDocument) {
return (R) read(type, (CouchbaseDocument) value, parent);
} else if (value instanceof CouchbaseList) {
return (R) readCollection(type, (CouchbaseList) value, parent);
} else {
return (R) getPotentiallyConvertedSimpleRead(value, rawType);
}
}
private ConvertingPropertyAccessor<Object> getPropertyAccessor(Object source) {
CouchbasePersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(source.getClass());
PersistentPropertyAccessor<Object> accessor = entity.getPropertyAccessor(source);
return new ConvertingPropertyAccessor<>(accessor, conversionService);
}
private String generateId(GeneratedValue generatedValue, TreeMap<Integer, String> prefixes,
TreeMap<Integer, String> suffixes, TreeMap<Integer, String> idAttributes) {
String delimiter = generatedValue.delimiter();
StringBuilder sb = new StringBuilder();
boolean isAppending = false;
if (prefixes.size() > 0) {
appendKeyParts(sb, prefixes.values(), delimiter);
isAppending = true;
}
if (generatedValue.strategy() == USE_ATTRIBUTES && idAttributes.size() > 0) {
if (isAppending) {
sb.append(delimiter);
}
appendKeyParts(sb, idAttributes.values(), delimiter);
isAppending = true;
}
if (generatedValue.strategy() == UNIQUE) {
if (isAppending) {
sb.append(delimiter);
}
sb.append(UUID.randomUUID());
isAppending = true;
}
if (suffixes.size() > 0) {
if (isAppending) {
sb.append(delimiter);
}
appendKeyParts(sb, suffixes.values(), delimiter);
}
return sb.toString();
}
private StringBuilder appendKeyParts(StringBuilder sb, Collection<String> values, String delimiter) {
boolean isAppending = false;
for (String value : values) {
if (isAppending) {
sb.append(delimiter);
} else {
isAppending = true;
}
sb.append(value);
}
return sb;
}
/**
* A property value provider for Couchbase documents.
*/
private class CouchbasePropertyValueProvider implements PropertyValueProvider<CouchbasePersistentProperty> {
/**
* The source document.
*/
private final CouchbaseDocument source;
/**
* The expression evaluator.
*/
private final SpELExpressionEvaluator evaluator;
/**
* The optional parent object.
*/
private final Object parent;
/**
* The entity of the property
*/
private final PersistentEntity entity;
public CouchbasePropertyValueProvider(final CouchbaseDocument source, final SpELContext factory,
final Object parent, final PersistentEntity entity) {
this(source, new DefaultSpELExpressionEvaluator(source, factory), parent, entity);
}
public CouchbasePropertyValueProvider(final CouchbaseDocument source,
final DefaultSpELExpressionEvaluator evaluator, final Object parent, final PersistentEntity entity) {
Assert.notNull(source, "CouchbaseDocument must not be null!");
Assert.notNull(evaluator, "DefaultSpELExpressionEvaluator must not be null!");
this.source = source;
this.evaluator = evaluator;
this.parent = parent;
this.entity = entity;
}
@Override
@SuppressWarnings("unchecked")
public <R> R getPropertyValue(final CouchbasePersistentProperty property) {
String expression = property.getSpelExpression();
Object value = expression != null ? evaluator.evaluate(expression) : source.get(property.getFieldName());
if (property == entity.getIdProperty() && parent == null) {
return (R) source.getId();
}
if (value == null) {
return null;
}
return readValue(value, property.getTypeInformation(), source);
}
}
/**
* A expression parameter value provider.
*/
private class ConverterAwareSpELExpressionParameterValueProvider
extends SpELExpressionParameterValueProvider<CouchbasePersistentProperty> {
private final Object parent;
public ConverterAwareSpELExpressionParameterValueProvider(final SpELExpressionEvaluator evaluator,
final ConversionService conversionService, final ParameterValueProvider<CouchbasePersistentProperty> delegate,
final Object parent) {
super(evaluator, conversionService, delegate);
this.parent = parent;
}
@Override
protected <T> T potentiallyConvertSpelValue(final Object object,
final Parameter<T, CouchbasePersistentProperty> parameter) {
return readValue(object, parameter.getType(), parent);
}
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright 2021-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
/**
* Out of the box conversions for java dates and calendars.
*
* @author Michael Reiche
*/
public final class OtherConverters {
private OtherConverters() {}
/**
* Returns all converters by this class that can be registered.
*
* @return the list of converters to register.
*/
public static Collection<Converter<?, ?>> getConvertersToRegister() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(UuidToString.INSTANCE);
converters.add(StringToUuid.INSTANCE);
converters.add(BigIntegerToString.INSTANCE);
converters.add(StringToBigInteger.INSTANCE);
converters.add(BigDecimalToString.INSTANCE);
converters.add(StringToBigDecimal.INSTANCE);
return converters;
}
@WritingConverter
public enum UuidToString implements Converter<UUID, String> {
INSTANCE;
@Override
public String convert(UUID source) {
return source == null ? null : source.toString();
}
}
@ReadingConverter
public enum StringToUuid implements Converter<String, UUID> {
INSTANCE;
@Override
public UUID convert(String source) {
return source == null ? null : UUID.fromString(source);
}
}
@WritingConverter
public enum BigIntegerToString implements Converter<BigInteger, String> {
INSTANCE;
@Override
public String convert(BigInteger source) {
return source == null ? null : source.toString();
}
}
@ReadingConverter
public enum StringToBigInteger implements Converter<String, BigInteger> {
INSTANCE;
@Override
public BigInteger convert(String source) {
return source == null ? null : new BigInteger(source);
}
}
@WritingConverter
public enum BigDecimalToString implements Converter<BigDecimal, String> {
INSTANCE;
@Override
public String convert(BigDecimal source) {
return source == null ? null : source.toString();
}
}
@ReadingConverter
public enum StringToBigDecimal implements Converter<String, BigDecimal> {
INSTANCE;
@Override
public BigDecimal convert(String source) {
return source == null ? null : new BigDecimal(source);
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.convert.SimpleTypeInformationMapper;
import org.springframework.data.mapping.Alias;
import org.springframework.data.util.TypeInformation;
/**
* TypeAwareTypeInformationMapper - leverages @TypeAlias
*
* @author Michael Reiche
*/
public class TypeAwareTypeInformationMapper extends SimpleTypeInformationMapper {
@Override
public Alias createAliasFor(TypeInformation<?> type) {
TypeAlias[] typeAlias = type.getType().getAnnotationsByType(TypeAlias.class);
if (typeAlias.length == 1) {
return Alias.of(typeAlias[0].value());
}
return super.createAliasFor(type);
}
}

View File

@@ -1,317 +0,0 @@
/*
* Copyright 2018-2022 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert.join;
import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_CAS;
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_ID;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.query.FetchType;
import org.springframework.data.couchbase.core.query.HashSide;
import org.springframework.data.couchbase.core.query.N1QLExpression;
import org.springframework.data.couchbase.core.query.N1QLQuery;
import org.springframework.data.couchbase.core.query.N1qlJoin;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.repository.Collection;
import org.springframework.data.couchbase.repository.Scope;
import org.springframework.data.couchbase.repository.query.StringBasedN1qlQueryParser;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.couchbase.client.core.io.CollectionIdentifier;
import com.couchbase.client.java.query.QueryOptions;
/**
* N1qlJoinResolver resolves by converting the join definition to query statement and executing using CouchbaseTemplate
*
* @author Subhashni Balakrishnan
* @author Michael Reiche
*/
public class N1qlJoinResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(N1qlJoinResolver.class);
public static <L, R> String buildQuery(ReactiveCouchbaseTemplate template, String scope, String collection,
N1qlJoinResolverParameters parameters) {
String joinType = "JOIN";
String selectEntity = "SELECT META(rks).id AS " + SELECT_ID + ", META(rks).cas AS " + SELECT_CAS + ", (rks).* ";
StringBuilder useLKSBuilder = new StringBuilder();
if (parameters.getJoinDefinition().index().length() > 0) {
useLKSBuilder.append("INDEX(" + parameters.getJoinDefinition().index() + ")");
}
String useLKS = useLKSBuilder.length() > 0 ? "USE " + useLKSBuilder.toString() + " " : "";
KeySpacePair keySpacePair = getKeySpacePair(template.getBucketName(), scope, collection, parameters);
String from = "FROM " + keySpacePair.lhs.keyspace + " lks " + useLKS + joinType + " " + keySpacePair.rhs.keyspace
+ " rks";
StringBasedN1qlQueryParser.N1qlSpelValues n1qlL = Query.getN1qlSpelValues(template.getConverter(), null, scope,
keySpacePair.lhs.collection, parameters.getEntityTypeInfo().getType(), parameters.getEntityTypeInfo().getType(),
false, null, null);
String onLks = "lks." + n1qlL.filter;
StringBasedN1qlQueryParser.N1qlSpelValues n1qlR = Query.getN1qlSpelValues(template.getConverter(), null, scope,
keySpacePair.rhs.collection, parameters.getAssociatedEntityTypeInfo().getType(),
parameters.getAssociatedEntityTypeInfo().getType(), false, null, null);
String onRks = "rks." + n1qlR.filter;
StringBuilder useRKSBuilder = new StringBuilder();
if (parameters.getJoinDefinition().rightIndex().length() > 0) {
useRKSBuilder.append("INDEX(" + parameters.getJoinDefinition().rightIndex() + ")");
}
if (!parameters.getJoinDefinition().hashside().equals(HashSide.NONE)) {
if (useRKSBuilder.length() > 0)
useRKSBuilder.append(" ");
useRKSBuilder.append("HASH(" + parameters.getJoinDefinition().hashside().getValue() + ")");
}
if (parameters.getJoinDefinition().keys().length > 0) {
if (useRKSBuilder.length() > 0)
useRKSBuilder.append(" ");
useRKSBuilder.append("KEYS [");
String[] keys = parameters.getJoinDefinition().keys();
for (int i = 0; i < keys.length; i++) {
if (i != 0)
useRKSBuilder.append(",");
useRKSBuilder.append("\"" + keys[i] + "\"");
}
useRKSBuilder.append("]");
}
String on = "ON " + parameters.getJoinDefinition().on().concat(" AND " + onLks).concat(" AND " + onRks);
String where = "WHERE META(lks).id=\"" + parameters.getLksId() + "\"";
where += ((parameters.getJoinDefinition().where().length() > 0) ? " AND " + parameters.getJoinDefinition().where()
: "");
StringBuilder statementSb = new StringBuilder();
statementSb.append(selectEntity);
statementSb.append(" " + from);
statementSb.append((useRKSBuilder.length() > 0 ? " USE " + useRKSBuilder.toString() : ""));
statementSb.append(" " + on);
statementSb.append(" " + where);
return statementSb.toString();
}
static KeySpacePair getKeySpacePair(String bucketName, String scope, String collection,
N1qlJoinResolverParameters parameters) {
Class<?> lhsClass = parameters.getEntityTypeInfo().getActualType().getType();
String lhScope = scope != null ? scope : getScope(lhsClass);
String lhCollection = collection != null ? collection : getCollection(lhsClass);
Class<?> rhsClass = parameters.getAssociatedEntityTypeInfo().getActualType().getType();
String rhScope = getScope(rhsClass);
String rhCollection = getCollection(rhsClass);
if (lhCollection != null && rhCollection != null) {
// they both have non-default collections
// It's possible that the scope for the lhs was set with an annotation on a repository method,
// the entity class or the repository class or a query option. Since there is no means to set
// the scope of the associated class by the method, repository class or query option (only
// the annotation) we assume that the (possibly) dynamic scope of the entity would be a better
// choice as it is logical to put collections to be joined in the same scope. Note that lhScope
// is used for both keyspaces.
return new KeySpacePair(lhCollection, x(i(bucketName) + "." + i(lhScope) + "." + i(lhCollection)), //
rhCollection, x(i(bucketName) + "." + i(lhScope) + "." + i(rhCollection)));
} else if (lhCollection != null && rhCollection == null) {
// the lhs has a collection (and therefore a scope as well), but the rhs does not have a collection.
// Use the lhScope and lhCollection for the entity. The rhs is just the bucket.
return new KeySpacePair(lhCollection, x(i(bucketName) + "." + i(lhScope) + "." + i(lhCollection)), //
null, i(bucketName));
} else if (lhCollection != null && rhCollection == null) {
// the lhs does not have a collection (or scope), but rhs does have a collection
// Using the same (default) scope for the rhs would mean specifying a
// non-default collection in a default scope - which is not allowed.
// So use the scope and collection from the associated class.
return new KeySpacePair(null, i(bucketName), //
rhCollection, x(i(bucketName) + "." + i(rhScope) + "." + i(rhCollection)));
} else { // neither have collections, just use the bucket.
return new KeySpacePair(null, i(bucketName), null, i(bucketName));
}
}
static class KeySpacePair {
KeySpaceInfo lhs;
KeySpaceInfo rhs;
public KeySpacePair(String lhsCollection, N1QLExpression lhsKeyspace, String rhsCollection,
N1QLExpression rhsKeyspace) {
this.lhs = new KeySpaceInfo(lhsCollection, lhsKeyspace);
this.rhs = new KeySpaceInfo(rhsCollection, rhsKeyspace);
}
static class KeySpaceInfo {
String collection;
N1QLExpression keyspace;
public KeySpaceInfo(String collection, N1QLExpression keyspace) {
this.collection = collection;
this.keyspace = keyspace;
}
}
}
/**
* from CouchbaseQueryMethod.getCollection()
*
* @param targetClass
* @return
*/
static String getCollection(Class<?> targetClass) {
// Could try the repository method, then the targetClass, then the repository class, then the entity class
// but we don't have the repository method nor the repositoryMetdata at this point.
AnnotatedElement[] annotated = new AnnotatedElement[] { targetClass };
return OptionsBuilder.annotationString(Collection.class, CollectionIdentifier.DEFAULT_COLLECTION, annotated);
}
/**
* from CouchbaseQueryMethod.getScope()
*
* @param targetClass
* @return
*/
static String getScope(Class<?> targetClass) {
// Could try the repository method, then the targetClass, then the repository class, then the entity class
// but we don't have the repository method nor the repositoryMetdata at this point.
AnnotatedElement[] annotated = new AnnotatedElement[] { targetClass };
return OptionsBuilder.annotationString(Scope.class, CollectionIdentifier.DEFAULT_SCOPE, annotated);
}
public static <R> List<R> doResolve(ReactiveCouchbaseTemplate template, String scopeName, String collectionName,
N1qlJoinResolverParameters parameters, Class<R> associatedEntityClass) {
String statement = buildQuery(template, scopeName, collectionName, parameters);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Join query executed " + statement);
}
N1QLQuery query = new N1QLQuery(N1QLExpression.x(statement), QueryOptions.queryOptions());
List<R> result = template.findByQuery(associatedEntityClass).matching(query).all().collectList().block();
return result.isEmpty() ? null : result;
}
public static boolean isLazyJoin(N1qlJoin joinDefinition) {
return joinDefinition.fetchType().equals(FetchType.LAZY);
}
public static void handleProperties(CouchbasePersistentEntity<?> persistentEntity,
ConvertingPropertyAccessor<?> accessor, ReactiveCouchbaseTemplate template, String id, String scope,
String collection) {
persistentEntity.doWithProperties((PropertyHandler<CouchbasePersistentProperty>) prop -> {
if (prop.isAnnotationPresent(N1qlJoin.class)) {
N1qlJoin definition = prop.findAnnotation(N1qlJoin.class);
TypeInformation type = prop.getTypeInformation().getActualType();
Class clazz = type.getType();
N1qlJoinResolver.N1qlJoinResolverParameters parameters = new N1qlJoinResolver.N1qlJoinResolverParameters(
definition, id, persistentEntity.getTypeInformation(), type, scope, collection);
if (N1qlJoinResolver.isLazyJoin(definition)) {
N1qlJoinResolver.N1qlJoinProxy proxy = new N1qlJoinResolver.N1qlJoinProxy(template, parameters);
accessor.setProperty(prop,
java.lang.reflect.Proxy.newProxyInstance(List.class.getClassLoader(), new Class[] { List.class }, proxy));
} else {
// clazz needs to be passes instead of just using
// parameters.associatedType.getTypeInformation().getActualType().getType
// to keep the compiler happy for the call template.findByQuery(associatedEntityClass)
accessor.setProperty(prop, N1qlJoinResolver.doResolve(template, scope, collection, parameters, clazz));
}
}
});
}
static public class N1qlJoinProxy implements InvocationHandler {
private final ReactiveCouchbaseTemplate reactiveTemplate;
private final String collectionName = null;
private final String scopeName = null;
private final N1qlJoinResolverParameters params;
private List<?> resolved = null;
public N1qlJoinProxy(ReactiveCouchbaseTemplate template, N1qlJoinResolverParameters params) {
this.reactiveTemplate = template;
this.params = params;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (this.resolved == null) {
this.resolved = doResolve(this.reactiveTemplate, this.params.getScopeName(), this.params.getCollectionName(),
this.params, this.params.associatedEntityTypeInfo.getType());
}
return method.invoke(this.resolved, args);
}
}
static public class N1qlJoinResolverParameters {
private N1qlJoin joinDefinition;
private String lksId;
private TypeInformation<?> entityTypeInfo;
private TypeInformation<?> associatedEntityTypeInfo;
private String scopeName;
private String collectionName;
public N1qlJoinResolverParameters(N1qlJoin joinDefinition, String lksId, TypeInformation<?> entityTypeInfo,
TypeInformation<?> associatedEntityTypeInfo, String scopeName, String collectionName) {
Assert.notNull(joinDefinition, "The join definition is required");
Assert.notNull(entityTypeInfo, "The entity type information is required");
Assert.notNull(associatedEntityTypeInfo, "The associated entity type information is required");
this.joinDefinition = joinDefinition;
this.lksId = lksId;
this.entityTypeInfo = entityTypeInfo;
this.associatedEntityTypeInfo = associatedEntityTypeInfo;
this.scopeName = scopeName;
this.collectionName = collectionName;
}
public N1qlJoin getJoinDefinition() {
return joinDefinition;
}
public String getLksId() {
return lksId;
}
public TypeInformation getEntityTypeInfo() {
return entityTypeInfo;
}
public TypeInformation getAssociatedEntityTypeInfo() {
return associatedEntityTypeInfo;
}
public String getScopeName() {
return scopeName;
}
public String getCollectionName() {
return collectionName;
}
}
}

View File

@@ -1,4 +0,0 @@
/**
* This package contains classes used for entity-to-JSON conversions, type mapping and writing.
*/
package org.springframework.data.couchbase.core.convert;

View File

@@ -1,252 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert.translation;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseList;
import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* A Jackson JSON Translator that implements the {@link TranslationService} contract.
*
* @author Michael Nitschinger
* @author Simon Baslé
* @author Anastasiia Smirnova
* @author Mark Paluch
*/
public class JacksonTranslationService implements TranslationService, InitializingBean {
/**
* Jackson Object Mapper;
*/
private ObjectMapper objectMapper;
/**
* Type holder to help easily identify simple types.
*/
private SimpleTypeHolder simpleTypeHolder = SimpleTypeHolder.DEFAULT;
/**
* JSON factory for Jackson.
*/
private JsonFactory factory = new JsonFactory();
/**
* Encode a {@link CouchbaseStorable} to a JSON string.
*
* @param source the source document to encode.
* @return the encoded JSON String.
*/
@Override
public final String encode(final CouchbaseStorable source) {
Writer writer = new StringWriter();
try {
JsonGenerator generator = factory.createGenerator(writer);
encodeRecursive(source, generator);
generator.close();
writer.close();
} catch (IOException ex) {
throw new RuntimeException("Could not encode JSON", ex);
}
return writer.toString();
}
/**
* Recursively iterates through the sources and adds it to the JSON generator.
*
* @param source the source document
* @param generator the JSON generator.
* @throws IOException
*/
private void encodeRecursive(final CouchbaseStorable source, final JsonGenerator generator) throws IOException {
generator.writeStartObject();
for (Map.Entry<String, Object> entry : ((CouchbaseDocument) source).export().entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
generator.writeFieldName(key);
if (value instanceof CouchbaseDocument) {
encodeRecursive((CouchbaseDocument) value, generator);
continue;
}
final Class<?> clazz = value.getClass();
if (simpleTypeHolder.isSimpleType(clazz) && !isEnumOrClass(clazz)) {
generator.writeObject(value);
} else {
objectMapper.writeValue(generator, value);
}
}
generator.writeEndObject();
}
private boolean isEnumOrClass(final Class<?> clazz) {
return Enum.class.isAssignableFrom(clazz) || Class.class.isAssignableFrom(clazz);
}
/**
* Decode a JSON string into the {@link CouchbaseStorable} structure.
*
* @param source the source formatted document.
* @param target the target of the populated data.
* @return the decoded structure.
*/
@Override
public final CouchbaseStorable decode(final String source, final CouchbaseStorable target) {
try {
JsonParser parser = factory.createParser((String) source);
while (parser.nextToken() != null) {
JsonToken currentToken = parser.getCurrentToken();
if (currentToken == JsonToken.START_OBJECT) {
return decodeObject(parser, (CouchbaseDocument) target);
} else if (currentToken == JsonToken.START_ARRAY) {
return decodeArray(parser, new CouchbaseList());
} else {
throw new MappingException("JSON to decode needs to start as array or object!");
}
}
parser.close();
} catch (IOException ex) {
throw new RuntimeException("Could not decode JSON", ex);
}
return target;
}
/**
* Helper method to decode an object recursively.
*
* @param parser the JSON parser with the content.
* @param target the target where the content should be stored.
* @throws IOException
* @returns the decoded object.
*/
private CouchbaseDocument decodeObject(final JsonParser parser, final CouchbaseDocument target) throws IOException {
JsonToken currentToken = parser.nextToken();
String fieldName = "";
while (currentToken != null && currentToken != JsonToken.END_OBJECT) {
if (currentToken == JsonToken.START_OBJECT) {
target.put(fieldName, decodeObject(parser, new CouchbaseDocument()));
} else if (currentToken == JsonToken.START_ARRAY) {
target.put(fieldName, decodeArray(parser, new CouchbaseList()));
} else if (currentToken == JsonToken.FIELD_NAME) {
fieldName = parser.getCurrentName();
} else {
target.put(fieldName, decodePrimitive(currentToken, parser));
}
currentToken = parser.nextToken();
}
return target;
}
/**
* Helper method to decode an array recusrively.
*
* @param parser the JSON parser with the content.
* @param target the target where the content should be stored.
* @throws IOException
* @returns the decoded list.
*/
private CouchbaseList decodeArray(final JsonParser parser, final CouchbaseList target) throws IOException {
JsonToken currentToken = parser.nextToken();
while (currentToken != null && currentToken != JsonToken.END_ARRAY) {
if (currentToken == JsonToken.START_OBJECT) {
target.put(decodeObject(parser, new CouchbaseDocument()));
} else if (currentToken == JsonToken.START_ARRAY) {
target.put(decodeArray(parser, new CouchbaseList()));
} else {
target.put(decodePrimitive(currentToken, parser));
}
currentToken = parser.nextToken();
}
return target;
}
/**
* Helper method to decode and assign a primitive.
*
* @param token the type of token.
* @param parser the parser with the content.
* @return the decoded primitve.
* @throws IOException
*/
private Object decodePrimitive(final JsonToken token, final JsonParser parser) throws IOException {
switch (token) {
case VALUE_TRUE:
case VALUE_FALSE:
return parser.getBooleanValue();
case VALUE_STRING:
return parser.getValueAsString();
case VALUE_NUMBER_INT:
return parser.getNumberValue();
case VALUE_NUMBER_FLOAT:
return parser.getDoubleValue();
case VALUE_NULL:
return null;
default:
throw new MappingException("Could not decode primitive value " + token);
}
}
@Override
public <T> T decodeFragment(String source, Class<T> target) {
try {
return objectMapper.readValue(source, target);
} catch (IOException e) {
throw new RuntimeException("Cannot decode ad-hoc JSON", e);
}
}
public void setObjectMapper(final ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void afterPropertiesSet() {
if (objectMapper == null) {
objectMapper = new ObjectMapper();
}
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.convert.translation;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
/**
* Defines a translation service to encode/decode responses into the {@link CouchbaseStorable} format.
*
* @author Michael Nitschinger
*/
public interface TranslationService {
/**
* Encodes a JSON String into the target format.
*
* @param source the source contents to encode.
* @return the encoded document representation.
*/
String encode(CouchbaseStorable source);
/**
* Decodes the target format into a {@link CouchbaseDocument}
*
* @param source the source formatted document.
* @param target the target of the populated data.
* @return a properly populated document to work with.
*/
CouchbaseStorable decode(String source, CouchbaseStorable target);
/**
* Decodes an ad-hoc JSON object into a corresponding "case" class.
*
* @param source the JSON for the ad-hoc JSON object (from a N1QL query for instance).
* @param target the target class information.
* @param <T> the target class.
* @return an ad-hoc instance of the decoded JSON into the corresponding "case" class.
*/
<T> T decodeFragment(String source, Class<T> target);
}

View File

@@ -1,5 +0,0 @@
/**
* This package contains a service interface to translate entities to a Couchbase storable format, and its
* implementations.
*/
package org.springframework.data.couchbase.core.convert.translation;

View File

@@ -1,35 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.index;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE })
@Documented
@Repeatable(CompositeQueryIndexes.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface CompositeQueryIndex {
String[] fields();
String name() default "";
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.index;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE })
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CompositeQueryIndexes {
CompositeQueryIndex[] value();
}

View File

@@ -1,132 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.index;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexResolver.IndexDefinitionHolder;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.MappingContextEvent;
import com.couchbase.client.core.error.IndexExistsException;
import com.couchbase.client.java.Cluster;
/**
* @author Michael Nitschinger
* @author Michael Reiche
*/
public class CouchbasePersistentEntityIndexCreator implements ApplicationListener<MappingContextEvent<?, ?>> {
private static final Logger LOGGER = LoggerFactory.getLogger(CouchbasePersistentEntityIndexCreator.class);
private final Map<Class<?>, Boolean> classesSeen = new ConcurrentHashMap<>();
private final CouchbaseMappingContext mappingContext;
private final QueryIndexResolver indexResolver;
private final CouchbaseOperations couchbaseOperations;
public CouchbasePersistentEntityIndexCreator(final CouchbaseMappingContext mappingContext,
final CouchbaseOperations operations) {
this.mappingContext = mappingContext;
this.couchbaseOperations = operations;
this.indexResolver = QueryIndexResolver.create(mappingContext, operations);
}
@Override
public void onApplicationEvent(final MappingContextEvent<?, ?> event) {
if (!event.wasEmittedBy(mappingContext)) {
return;
}
PersistentEntity<?, ?> entity = event.getPersistentEntity();
// Double check type as Spring infrastructure does not consider nested generics
if (entity instanceof CouchbasePersistentEntity) {
checkForIndexes((CouchbasePersistentEntity<?>) entity);
}
}
private void checkForIndexes(final CouchbasePersistentEntity<?> entity) {
Class<?> type = entity.getType();
if (!classesSeen.containsKey(type)) {
this.classesSeen.put(type, Boolean.TRUE);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Analyzing class " + type + " for index information.");
}
checkForAndCreateIndexes(entity);
}
}
private void checkForAndCreateIndexes(final CouchbasePersistentEntity<?> entity) {
if (entity.isAnnotationPresent(Document.class)) {
for (IndexDefinition indexDefinition : indexResolver.resolveIndexFor(entity.getTypeInformation())) {
IndexDefinitionHolder indexToCreate = indexDefinition instanceof IndexDefinitionHolder
? (IndexDefinitionHolder) indexDefinition
: new IndexDefinitionHolder(indexDefinition.getIndexFields(), indexDefinition.getIndexName(),
indexDefinition.getIndexPredicate());
createIndex(indexToCreate);
}
}
}
private void createIndex(final IndexDefinitionHolder indexToCreate) {
Cluster cluster = couchbaseOperations.getCouchbaseClientFactory().getCluster();
StringBuilder statement = new StringBuilder("CREATE INDEX `")
.append(indexToCreate.getIndexName()).append("` ON `")
.append(couchbaseOperations.getBucketName()).append("` (")
.append(String.join(",", indexToCreate.getIndexFields())).append(")");
if (indexToCreate.getIndexPredicate() != null && !indexToCreate.getIndexPredicate().isEmpty()) {
statement.append(" WHERE ").append(indexToCreate.getIndexPredicate());
}
try {
cluster.query(statement.toString());
} catch (IndexExistsException ex) {
// ignored on purpose, rest is propagated
LOGGER.debug("Index \"" + indexToCreate.getIndexName() + "\" already exists, ignoring.");
} catch (Exception ex) {
throw new DataIntegrityViolationException("Could not auto-create index with statement: " + statement.toString(),
ex);
}
}
/**
* Returns whether the current index creator was registered for the given {@link MappingContext}.
*/
public boolean isIndexCreatorFor(final MappingContext<?, ?> context) {
return this.mappingContext.equals(context);
}
public boolean hasSeen(CouchbasePersistentEntity<?> entity) {
return classesSeen.containsKey(entity.getType());
}
}

View File

@@ -1,177 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.index;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.repository.support.MappingCouchbaseEntityInformation;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Michael Nitschinger
* @author Michael Reiche
*/
public class CouchbasePersistentEntityIndexResolver implements QueryIndexResolver {
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
private final CouchbaseOperations operations;
public CouchbasePersistentEntityIndexResolver(
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
CouchbaseOperations operations) {
this.mappingContext = mappingContext;
this.operations = operations;
}
@Override
public Iterable<? extends IndexDefinitionHolder> resolveIndexFor(final TypeInformation<?> typeInformation) {
return resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(typeInformation));
}
public List<IndexDefinitionHolder> resolveIndexForEntity(final CouchbasePersistentEntity<?> root) {
Assert.notNull(root, "CouchbasePersistentEntity must not be null!");
Document document = root.findAnnotation(Document.class);
Assert.notNull(document, () -> String
.format("Entity %s is not a collection root. Make sure to annotate it with @Document!", root.getName()));
List<IndexDefinitionHolder> indexInformation = new ArrayList<>();
root.doWithProperties((PropertyHandler<CouchbasePersistentProperty>) property -> this
.potentiallyAddIndexForProperty(root, property, indexInformation));
return indexInformation;
}
private void potentiallyAddIndexForProperty(final CouchbasePersistentEntity<?> root,
final CouchbasePersistentProperty persistentProperty, final List<IndexDefinitionHolder> indexes) {
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(
persistentProperty.getFieldName(), root, persistentProperty);
if (!indexDefinitions.isEmpty()) {
indexes.addAll(indexDefinitions);
}
}
private List<IndexDefinitionHolder> createIndexDefinitionHolderForProperty(final String dotPath,
final CouchbasePersistentEntity<?> persistentEntity, final CouchbasePersistentProperty persistentProperty) {
List<IndexDefinitionHolder> indices = new ArrayList<>();
if (persistentProperty.isAnnotationPresent(QueryIndexed.class)) {
indices.add(createFieldQueryIndexDefinition(persistentEntity, persistentProperty));
}
if (persistentEntity.isAnnotationPresent(CompositeQueryIndex.class)
|| persistentEntity.isAnnotationPresent(CompositeQueryIndexes.class)) {
indices.addAll(createCompositeQueryIndexDefinitions(persistentEntity, persistentProperty));
}
return indices;
}
@Nullable
protected IndexDefinitionHolder createFieldQueryIndexDefinition(final CouchbasePersistentEntity<?> entity,
final CouchbasePersistentProperty property) {
QueryIndexed index = property.findAnnotation(QueryIndexed.class);
if (index == null) {
return null;
}
MappingCouchbaseEntityInformation<?, Object> entityInfo = new MappingCouchbaseEntityInformation<>(entity);
List<String> fields = new ArrayList<>();
String fieldName = index.name().isEmpty() ? property.getFieldName() : index.name();
fields.add(fieldName + (index.direction() == QueryIndexDirection.DESCENDING ? " DESC" : ""));
String indexName = "idx_" + StringUtils.uncapitalize(entity.getType().getSimpleName()) + "_"
+ fieldName.replace(".", "_");
return new IndexDefinitionHolder(fields, indexName, getPredicate(entityInfo));
}
protected List<IndexDefinitionHolder> createCompositeQueryIndexDefinitions(final CouchbasePersistentEntity<?> entity,
final CouchbasePersistentProperty property) {
List<CompositeQueryIndex> indexAnnotations = new ArrayList<>();
if (entity.isAnnotationPresent(CompositeQueryIndex.class)) {
indexAnnotations.add(entity.findAnnotation(CompositeQueryIndex.class));
}
if (entity.isAnnotationPresent(CompositeQueryIndexes.class)) {
indexAnnotations.addAll(Arrays.asList(entity.findAnnotation(CompositeQueryIndexes.class).value()));
}
MappingCouchbaseEntityInformation<?, Object> entityInfo = new MappingCouchbaseEntityInformation<>(entity);
String predicate = getPredicate(entityInfo);
return indexAnnotations.stream().map(ann -> {
List<String> fields = Arrays.asList(ann.fields());
String fieldsIndexName = String.join("_", fields).toLowerCase().replace(".", "_").replace(" ", "")
.replace("asc", "").replace("desc", "");
String indexName = "idx_" + StringUtils.uncapitalize(entity.getType().getSimpleName()) + "_" + fieldsIndexName;
return new IndexDefinitionHolder(fields, indexName, predicate);
}).collect(Collectors.toList());
}
private String getPredicate(final MappingCouchbaseEntityInformation<?, Object> entityInfo) {
String typeKey = operations.getConverter().getTypeKey();
String typeValue = entityInfo.getJavaType().getName();
return "`" + typeKey + "` = \"" + typeValue + "\"";
}
public static class IndexDefinitionHolder implements IndexDefinition {
private final List<String> fields;
private final String indexName;
private final String indexPredicate;
public IndexDefinitionHolder(List<String> fields, String indexName, String indexPredicate) {
this.fields = fields;
this.indexName = indexName;
this.indexPredicate = indexPredicate;
}
@Override
public List<String> getIndexFields() {
return fields;
}
@Override
public String getIndexName() {
return indexName;
}
@Override
public String getIndexPredicate() {
return indexPredicate;
}
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright 2011-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.index;
import java.util.List;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Christoph Strobl
* @author Mark Paluch
*/
public interface IndexDefinition {
String getIndexName();
List<String> getIndexFields();
String getIndexPredicate();
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.index;
public enum QueryIndexDirection {
ASCENDING, DESCENDING
}

Some files were not shown because too many files have changed in this diff Show More