diff --git a/src/integration/java/org/springframework/data/couchbase/IntegrationTestApplicationConfig.java b/src/integration/java/org/springframework/data/couchbase/IntegrationTestApplicationConfig.java index f9d8e863..8dee2744 100644 --- a/src/integration/java/org/springframework/data/couchbase/IntegrationTestApplicationConfig.java +++ b/src/integration/java/org/springframework/data/couchbase/IntegrationTestApplicationConfig.java @@ -64,4 +64,10 @@ public class IntegrationTestApplicationConfig extends AbstractCouchbaseConfigura template.setWriteResultChecking(WriteResultChecking.LOG); return template; } + + //change the name of the field that will hold type information + @Override + public String typeKey() { + return "javaClass"; + } } diff --git a/src/integration/java/org/springframework/data/couchbase/config/CouchbaseTemplateParserIntegrationTests.java b/src/integration/java/org/springframework/data/couchbase/config/CouchbaseTemplateParserIntegrationTests.java index c235f80a..8a8de793 100644 --- a/src/integration/java/org/springframework/data/couchbase/config/CouchbaseTemplateParserIntegrationTests.java +++ b/src/integration/java/org/springframework/data/couchbase/config/CouchbaseTemplateParserIntegrationTests.java @@ -16,8 +16,10 @@ package org.springframework.data.couchbase.config; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; +import com.couchbase.client.java.document.JsonDocument; +import com.couchbase.client.java.document.json.JsonObject; import org.junit.Before; import org.junit.Test; @@ -26,6 +28,9 @@ import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; +import org.springframework.data.couchbase.core.CouchbaseTemplate; +import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; +import org.springframework.data.couchbase.repository.User; /** * @author Michael Nitschinger @@ -72,4 +77,27 @@ public class CouchbaseTemplateParserIntegrationTests { factory.getBean("cb-template-second"); } + /** + * Test case for DATACOUCH-134 in xml: field for storing type information can be renamed. + */ + @Test + public void testTypeFieldCanBeChosen() { + reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-typekey.xml")); + CouchbaseTemplate template = factory.getBean("couchbaseTemplate", CouchbaseTemplate.class); + + assertTrue(template.getConverter() instanceof MappingCouchbaseConverter); + MappingCouchbaseConverter converter = ((MappingCouchbaseConverter) template.getConverter()); + + assertEquals("javaXmlClass", converter.getTypeKey()); + + User u = new User("specialSaveUser", "John Locke"); + template.save(u); + JsonDocument uJsonDoc = template.getCouchbaseBucket().get("specialSaveUser"); + template.getCouchbaseBucket().remove("specialSaveUser"); + assertNotNull(uJsonDoc); + JsonObject uJson = uJsonDoc.content(); + assertNull(uJson.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT)); + assertEquals("org.springframework.data.couchbase.repository.User", uJson.getString("javaXmlClass")); + assertEquals("John Locke", uJson.getString("username")); + } } diff --git a/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java b/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java index 9876ce6d..75b5d632 100644 --- a/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java +++ b/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java @@ -50,6 +50,7 @@ import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Version; import org.springframework.data.couchbase.IntegrationTestApplicationConfig; +import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; import org.springframework.data.couchbase.core.mapping.Document; import org.springframework.data.couchbase.core.mapping.Field; import org.springframework.test.context.ContextConfiguration; @@ -95,7 +96,9 @@ public class CouchbaseTemplateTests { assertNotNull(result); Map resultConv = MAPPER.readValue(result, new TypeReference>() {}); - assertEquals("org.springframework.data.couchbase.core.Beer", resultConv.get("_class")); + //DATACOUCH-134: the class holding field can be changed + assertNull(resultConv.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT)); + assertEquals("org.springframework.data.couchbase.core.Beer", resultConv.get("javaClass")); assertEquals(false, resultConv.get("is_active")); assertEquals("The Awesome Stout", resultConv.get("name")); } diff --git a/src/integration/resources/configurations/couchbase-typekey.xml b/src/integration/resources/configurations/couchbase-typekey.xml new file mode 100644 index 00000000..dfe24e6b --- /dev/null +++ b/src/integration/resources/configurations/couchbase-typekey.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java index 274638f7..26721d25 100644 --- a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java +++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java @@ -137,6 +137,18 @@ public abstract class AbstractCouchbaseConfiguration { return new CouchbaseTemplate(couchbaseClient(), mappingCouchbaseConverter(), translationService()); } + /** + * Determines the name of the field that will store the type information for complex types when + * using the {@link #mappingCouchbaseConverter()}. + * 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}. * @@ -144,7 +156,7 @@ public abstract class AbstractCouchbaseConfiguration { */ @Bean public MappingCouchbaseConverter mappingCouchbaseConverter() throws Exception { - MappingCouchbaseConverter converter = new MappingCouchbaseConverter(couchbaseMappingContext()); + MappingCouchbaseConverter converter = new MappingCouchbaseConverter(couchbaseMappingContext(), typeKey()); converter.setCustomConversions(customConversions()); return converter; } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseTypeMapper.java b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseTypeMapper.java index 93615e11..e2cc10f0 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseTypeMapper.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseTypeMapper.java @@ -26,4 +26,6 @@ import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; */ public interface CouchbaseTypeMapper extends TypeMapper { + String getTypeKey(); + } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/DefaultCouchbaseTypeMapper.java b/src/main/java/org/springframework/data/couchbase/core/convert/DefaultCouchbaseTypeMapper.java index 004a241a..68fc6d05 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/DefaultCouchbaseTypeMapper.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/DefaultCouchbaseTypeMapper.java @@ -32,6 +32,8 @@ public class DefaultCouchbaseTypeMapper extends DefaultTypeMapper { diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java index 75919923..a5e33171 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java @@ -64,6 +64,19 @@ import org.springframework.util.CollectionUtils; 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 overall application context. */ @@ -90,13 +103,24 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter * * @param mappingContext the mapping context to use. */ - @SuppressWarnings("deprecation") public MappingCouchbaseConverter(final MappingContext, - CouchbasePersistentProperty> mappingContext) { + CouchbasePersistentProperty> mappingContext) { + this(mappingContext, TYPEKEY_DEFAULT); + } + + /** + * Create a new {@link MappingCouchbaseConverter} that will store class name for + * complex types in the typeKey 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, + CouchbasePersistentProperty> mappingContext, final String typeKey) { super(new DefaultConversionService()); this.mappingContext = mappingContext; - typeMapper = new DefaultCouchbaseTypeMapper(DefaultCouchbaseTypeMapper.DEFAULT_TYPE_KEY); + typeMapper = new DefaultCouchbaseTypeMapper(typeKey != null ? typeKey : TYPEKEY_DEFAULT); spELContext = new SpELContext(CouchbaseDocumentPropertyAccessor.INSTANCE); } @@ -105,6 +129,13 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter return mappingContext; } + /** + * @return the name of the field that will hold type information. + */ + public String getTypeKey() { + return typeMapper.getTypeKey(); + } + @Override public R read(final Class clazz, final CouchbaseDocument source) { return read(ClassTypeInformation.from(clazz), source, null);