DATACOUCH-525 - support custom type field name and custome type field value

MOTIVATION
customers would like to specify a type field other than _class, and
would like to specify a type field value other than the class name
of the domain object

CHANGES
Follow the instructions at
https://stackoverflow.com/questions/38847605/how-to-use-spring-data-with-couchbase-without-class-attribute
in the test domain, supply a CustomMappingCouchbaseConvert class that leverates a
TypeBasedCouchbaseTypeMapper, which in turn uses a TypeAwareTypeInformationMapper.
The typeKey used is "t". The TypeAwareTypeInformationMapper uses the DocumentType annotation
as the value for the type map entry. The domain Person object has @DocumentType("p"), which
will result in type mapping of "t" : "p" for Person objects.

In addition, when a query is composed, the typeKey and typeValue are taken from the converter.
This commit is contained in:
mikereiche
2020-06-02 15:59:57 -07:00
parent dafff211a4
commit 95c69ea8cc
11 changed files with 231 additions and 23 deletions

View File

@@ -21,6 +21,8 @@ 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.
@@ -54,4 +56,9 @@ public interface CouchbaseConverter
* @return the name of the field that will hold type information.
*/
String getTypeKey();
/**
* @return the alias value for the type
*/
Alias getTypeAlias(TypeInformation<?> info);
}

View File

@@ -18,14 +18,19 @@ 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

@@ -20,6 +20,7 @@ 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.util.TypeInformation;
/**
* The Couchbase Type Mapper.
@@ -72,4 +73,8 @@ public class DefaultCouchbaseTypeMapper extends DefaultTypeMapper<CouchbaseDocum
}
}
@Override
public Alias getTypeAlias(TypeInformation<?> info) {
return getAliasFor(info);
}
}

View File

@@ -39,17 +39,14 @@ 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.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.*;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
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;
@@ -182,6 +179,11 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
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);

View File

@@ -31,6 +31,9 @@ import org.springframework.data.couchbase.repository.query.StringBasedN1qlQueryP
import org.springframework.data.couchbase.repository.support.MappingCouchbaseEntityInformation;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.Alias;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
@@ -47,8 +50,7 @@ public class Query {
static private final Pattern WHERE_PATTERN = Pattern.compile("\\sWHERE\\s");
public Query() {
}
public Query() {}
public Query(final QueryCriteria criteriaDefinition) {
addCriteria(criteriaDefinition);
@@ -60,8 +62,8 @@ public class Query {
}
/**
* set the postional parameters on the query object
* There can only be named parameters or positional parameters - not both.
* set the postional parameters on the query object There can only be named parameters or positional parameters - not
* both.
*
* @param parameters - the positional parameters
* @return - the query
@@ -72,8 +74,8 @@ public class Query {
}
/**
* set the named parameters on the query object
* There can only be named parameters or positional parameters - not both.
* set the named parameters on the query object There can only be named parameters or positional parameters - not
* both.
*
* @param parameters - the named parameters
* @return - the query
@@ -157,10 +159,8 @@ public class Query {
sb.append(" ORDER BY ");
sort.stream().forEach(order -> {
if (order.isIgnoreCase()) {
throw new IllegalArgumentException(String.format(
"Given sort contained an Order for %s with ignore case! "
+ "Couchbase N1QL does not support sorting ignoring case currently!",
order.getProperty()));
throw new IllegalArgumentException(String.format("Given sort contained an Order for %s with ignore case! "
+ "Couchbase N1QL does not support sorting ignoring case currently!", order.getProperty()));
}
sb.append(order.getProperty()).append(" ").append(order.isAscending() ? "ASC," : "DESC,");
});
@@ -257,10 +257,15 @@ public class Query {
StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTemplate template, Class domainClass,
boolean isCount) {
String typeKey = template.getConverter().getTypeKey();
final CouchbasePersistentEntity<?> persistentEntity = template.getConverter().getMappingContext().getRequiredPersistentEntity(
domainClass);
final CouchbasePersistentEntity<?> persistentEntity = template.getConverter().getMappingContext()
.getRequiredPersistentEntity(domainClass);
MappingCouchbaseEntityInformation<?, Object> info = new MappingCouchbaseEntityInformation<>(persistentEntity);
String typeValue = info.getJavaType().getName();
TypeInformation<?> typeInfo = ClassTypeInformation.from(info.getJavaType());
Alias alias = template.getConverter().getTypeAlias(typeInfo);
if (alias != null && alias.isPresent()) {
typeValue = alias.toString();
}
return StringBasedN1qlQueryParser.createN1qlSpelValues(template.getBucketName(), typeKey, typeValue, isCount);
}