DATACOUCH-134 - allow changing the name of field storing type.

This commit is contained in:
Simon Baslé
2015-07-07 11:38:24 +02:00
parent c29727595c
commit 2bf2d3e544
8 changed files with 126 additions and 6 deletions

View File

@@ -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";
}
}

View File

@@ -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"));
}
}

View File

@@ -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<String, Object> resultConv = MAPPER.readValue(result, new TypeReference<Map<String, Object>>() {});
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"));
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:couchbase="http://www.springframework.org/schema/data/couchbase"
xsi:schemaLocation="http://www.springframework.org/schema/data/couchbase http://www.springframework.org/schema/data/couchbase/spring-couchbase.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<couchbase:env/>
<couchbase:cluster/>
<couchbase:bucket/>
<!--note that the template needs both converter and translation service if you want to customize converter-->
<couchbase:template translation-service-ref="myCustomTranslationService" converter-ref="myCustomConverter"/>
<bean id="myCustomTranslationService" class="org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService"/>
<bean id="myCustomConverter" class="org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter">
<!--this is a consequence of having to customize the converter-->
<!--WARNING this example is incomplete, look at couchbaseMappingContext() in AbstractCouchbaseConfiguration -->
<constructor-arg index="0" type="org.springframework.data.mapping.context.MappingContext">
<ref bean="myCustomMappingContext"/>
</constructor-arg>
<!--this is where you customize the class field in JSON -->
<constructor-arg index="1" type="java.lang.String" value="javaXmlClass"/>
</bean>
<!--WARNING this example is incomplete, look at couchbaseMappingContext() in AbstractCouchbaseConfiguration -->
<bean id="myCustomMappingContext" class="org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext" />
</beans>

View File

@@ -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;
}

View File

@@ -26,4 +26,6 @@ import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
*/
public interface CouchbaseTypeMapper extends TypeMapper<CouchbaseDocument> {
String getTypeKey();
}

View File

@@ -32,6 +32,8 @@ public class DefaultCouchbaseTypeMapper extends DefaultTypeMapper<CouchbaseDocum
*/
public static final String DEFAULT_TYPE_KEY = "_class";
private final String typeKey;
/**
* Create a new type mapper with the type key.
*
@@ -39,6 +41,12 @@ public class DefaultCouchbaseTypeMapper extends DefaultTypeMapper<CouchbaseDocum
*/
public DefaultCouchbaseTypeMapper(final String typeKey) {
super(new CouchbaseDocumentTypeAliasAccessor(typeKey));
this.typeKey = typeKey;
}
@Override
public String getTypeKey() {
return this.typeKey;
}
public static final class CouchbaseDocumentTypeAliasAccessor implements TypeAliasAccessor<CouchbaseDocument> {

View File

@@ -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<? extends CouchbasePersistentEntity<?>,
CouchbasePersistentProperty> mappingContext) {
CouchbasePersistentProperty> mappingContext) {
this(mappingContext, TYPEKEY_DEFAULT);
}
/**
* 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;
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> R read(final Class<R> clazz, final CouchbaseDocument source) {
return read(ClassTypeInformation.from(clazz), source, null);