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 e9c48f31..9e5bc8b0 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 @@ -863,6 +863,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem sb.append(delimiter); } appendKeyParts(sb, idAttributes.values(), delimiter); + isAppending = true; } if (generatedValue.strategy() == UNIQUE) { @@ -870,6 +871,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem sb.append(delimiter); } sb.append(UUID.randomUUID()); + isAppending = true; } if (suffixes.size() > 0) { diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/MappingCouchbaseConverterTests.java b/src/test/java/org/springframework/data/couchbase/core/mapping/MappingCouchbaseConverterTests.java index cb650cb5..f868c904 100644 --- a/src/test/java/org/springframework/data/couchbase/core/mapping/MappingCouchbaseConverterTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/MappingCouchbaseConverterTests.java @@ -34,9 +34,7 @@ import org.springframework.data.convert.WritingConverter; import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; import org.springframework.data.couchbase.core.convert.CouchbaseJsr310Converters.LocalDateTimeToLongConverter; import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; -import org.springframework.data.couchbase.core.mapping.id.GeneratedValue; -import org.springframework.data.couchbase.core.mapping.id.GenerationStrategy; -import org.springframework.data.couchbase.core.mapping.id.IdPrefix; +import org.springframework.data.couchbase.core.mapping.id.*; import org.springframework.data.couchbase.domain.Config; import org.springframework.data.couchbase.domain.User; import org.springframework.data.mapping.MappingException; @@ -788,89 +786,140 @@ public class MappingCouchbaseConverterTests { } @Test - void idTest00() { // id does not get set + void idNotGenerated() { + class Entity { + public static final String ID = "mockid"; + @Id private String id = ID; + } + Entity entity = new Entity(); CouchbaseDocument converted = new CouchbaseDocument(); - IdTest00Entity entity = new IdTest00Entity(); converter.write(entity, converted); Map result = converted.export(); - assertThat(converted.getId()).isEqualTo(entity.getId()); - } - - public static class IdTest00Entity { // id does not get set - public static final String ID = "mockid"; - @Id private String id = ID; - - String getId() { - return id; - } + assertThat(converted.getId()).isEqualTo(entity.id); } @Test - void idTest02() { // id is String + void idIsGeneratedString() { + class Entity { + @GeneratedValue(strategy = GenerationStrategy.UNIQUE) @Id String id; + } + Entity entity = new Entity(); CouchbaseDocument converted = new CouchbaseDocument(); - IdTest02Entity entity = new IdTest02Entity(); converter.write(entity, converted); Map result = converted.export(); - assertThat(converted.getId()).isEqualTo(entity.getId()); - } - - public static class IdTest02Entity { // id is String - @GeneratedValue(strategy = GenerationStrategy.UNIQUE) @Id String id; - - String getId() { - return id; - } + assertThat(converted.getId()).isEqualTo(entity.id); } @Test - void idTest03() { // id is UUID - CouchbaseDocument converted = new CouchbaseDocument(); - IdTest03Entity entity = new IdTest03Entity(); - converter.write(entity, converted); - assertThat(converted.getId()).isEqualTo(entity.getId()); - } - - public static class IdTest03Entity { // id is UUID - @GeneratedValue(strategy = GenerationStrategy.UNIQUE) @Id UUID id; - - String getId() { - return id.toString(); + void idIsGeneratedUUID() { + class Entity { + @GeneratedValue(strategy = GenerationStrategy.UNIQUE) @Id UUID id; } - + Entity entity = new Entity(); + CouchbaseDocument converted = new CouchbaseDocument(); + converter.write(entity, converted); + assertThat(converted.getId()).isEqualTo(entity.id.toString()); } @Test - void idTest05() { // id is Integer - CouchbaseDocument converted = new CouchbaseDocument(); - IdTest05Entity entity = new IdTest05Entity(); - converter.write(entity, converted); - assertThat(converted.getId()).isEqualTo(entity.getId()); - } - - public static class IdTest05Entity { // id is Integer - @GeneratedValue() @Id Integer id; - @IdPrefix public String prefix = "123"; - - String getId() { - return id.toString(); + void idIsGeneratedInteger() { + class Entity { + @GeneratedValue() @Id Integer id; // cannot be UNIQUE since a UUID cannot be coerced into an Integer + @IdAttribute public String attribute = "111"; // here's my Integer } + Entity entity = new Entity(); + CouchbaseDocument converted = new CouchbaseDocument(); + converter.write(entity, converted); + assertThat(converted.getId()).isEqualTo(entity.id.toString()); } @Test - void idTest07() { // id is Number - CouchbaseDocument converted = new CouchbaseDocument(); - IdTest07Entity entity = new IdTest07Entity(); - converter.write(entity, converted); - assertThat(converted.getId()).isEqualTo(entity.getId()); - } - - public static class IdTest07Entity { // id is Number - @GeneratedValue() @Id public Number id; - @IdPrefix public String prefix = "123"; - - String getId() { - return id.toString(); + void idIsGeneratedNumber() { + class Entity { + @GeneratedValue() @Id public Number id; // cannot be UNIQUE since a UUID cannot be coerced into a Number + @IdAttribute public String attribute = "111"; // here's my number } + Entity entity = new Entity(); + CouchbaseDocument converted = new CouchbaseDocument(); + converter.write(entity, converted); + assertThat(converted.getId()).isEqualTo(entity.id.toString()); } + @Test + void idHasPrefix() { + class Entity { + @GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES) @Id public String id; + @IdAttribute public String someId = "abc"; + @IdPrefix public String prefix = "111"; + } + Entity entity = new Entity(); + CouchbaseDocument converted = new CouchbaseDocument(); + converter.write(entity, converted); + assertThat(converted.getId()).isEqualTo(entity.id); + assertThat(converted.getId()).isEqualTo(entity.prefix + '.' + entity.someId); + } + + @Test + void idHasSuffix() { + class Entity { + @GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES) @Id public String id; + @IdAttribute public String someId = "abc"; + @IdSuffix public String suffix = "999"; + } + Entity entity = new Entity(); + CouchbaseDocument converted = new CouchbaseDocument(); + converter.write(entity, converted); + assertThat(converted.getId()).isEqualTo(entity.id); + assertThat(converted.getId()).isEqualTo(entity.someId + '.' + entity.suffix); + } + + @Test + void idHasPrefixAndSuffix() { + class Entity { + @GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES) @Id public String id; + @IdAttribute public String someId = "abc"; + @IdPrefix public String prefix = "111"; + @IdSuffix public String suffix = "999"; + } + Entity entity = new Entity(); + CouchbaseDocument converted = new CouchbaseDocument(); + converter.write(entity, converted); + assertThat(converted.getId()).isEqualTo(entity.id); + assertThat(converted.getId()).isEqualTo(entity.prefix + '.' + entity.someId + '.' + entity.suffix); + } + + @Test + void idHasMultiplePrefixesAndSuffixes() { + class Entity { + @GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES) @Id public String id; + @IdAttribute public String someId = "abc"; + @IdPrefix(order = 1) public String prefix = "111"; + @IdPrefix(order = 3) public String prefix1 = "333"; + @IdSuffix(order = 4) public String suffix1 = "444"; + @IdSuffix(order = 2) public String suffix = "222"; + } + Entity entity = new Entity(); + CouchbaseDocument converted = new CouchbaseDocument(); + converter.write(entity, converted); + assertThat(converted.getId()).isEqualTo(entity.id); + assertThat(converted.getId()).isEqualTo( + entity.prefix + '.' + entity.prefix1 + '.' + entity.someId + '.' + entity.suffix + '.' + entity.suffix1); + } + + @Test + void idHasMultiplePrefixesAndSuffixesSameOrder() { // might want to disallow this + class Entity { + @GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES) @Id public String id; + @IdAttribute public String someId = "abc"; + @IdPrefix(order = 1) public String prefix = "111"; + @IdPrefix(order = 1) public String prefix1 = "333"; + @IdSuffix(order = 1) public String suffix1 = "444"; + @IdSuffix(order = 1) public String suffix = "222"; + } + Entity entity = new Entity(); + CouchbaseDocument converted = new CouchbaseDocument(); + converter.write(entity, converted); + assertThat(converted.getId()).isEqualTo(entity.id); + assertThat(converted.getId()).isEqualTo(entity.prefix1 + '.' + entity.someId + '.' + entity.suffix); + } } diff --git a/src/test/java/org/springframework/data/couchbase/domain/Config.java b/src/test/java/org/springframework/data/couchbase/domain/Config.java index 2864eff9..67ce065d 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/Config.java +++ b/src/test/java/org/springframework/data/couchbase/domain/Config.java @@ -16,17 +16,31 @@ package org.springframework.data.couchbase.domain; -import org.springframework.data.couchbase.config.BeanNames; -import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; -import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; -import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; +import com.couchbase.client.java.Cluster; +import com.couchbase.client.java.ClusterOptions; +import com.couchbase.client.java.env.ClusterEnvironment; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.auditing.DateTimeProvider; +import org.springframework.data.convert.CustomConversions; +import org.springframework.data.couchbase.CouchbaseClientFactory; +import org.springframework.data.couchbase.SimpleCouchbaseClientFactory; import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; +import org.springframework.data.couchbase.config.BeanNames; +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.mapping.CouchbaseMappingContext; import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider; import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing; import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories; +import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping; +import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; +import org.springframework.data.util.TypeInformation; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; /** * @author Michael Nitschinger @@ -48,64 +62,42 @@ public class Config extends AbstractCouchbaseConfiguration { static { try { clusterAware = Class.forName("org.springframework.data.couchbase.util.ClusterAwareIntegrationTests"); - } catch (ClassNotFoundException cnfe) { + if (clusterAware.getMethod("config").invoke(null) == null) + clusterAware = null; + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); } } + String clusterGet( String methodName, String defaultValue){ + if (clusterAware != null) { + try { + return (String) clusterAware.getMethod(methodName).invoke(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return defaultValue; + } + @Override public String getConnectionString() { - if (clusterAware != null) { - try { - if (clusterAware.getMethod("config").invoke(null, (Object[]) null) != null) { - return (String) clusterAware.getMethod("connectionString").invoke(null, (Object[]) null); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return connectionString; + return clusterGet( "connectionString", connectionString ); } @Override public String getUserName() { - if (clusterAware != null) { - try { - if (clusterAware.getMethod("config").invoke(null, (Object[]) null) != null) { - return (String) clusterAware.getMethod("username").invoke(null, (Object[]) null); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return username; + return clusterGet( "username", username ); } @Override public String getPassword() { - if (clusterAware != null) { - try { - if (clusterAware.getMethod("config").invoke(null, (Object[]) null) != null) { - return (String) clusterAware.getMethod("password").invoke(null, (Object[]) null); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return password; + return clusterGet( "password", password ); } @Override public String getBucketName() { - if (clusterAware != null) { - try { - if (clusterAware.getMethod("config").invoke(null, (Object[]) null) != null) { - return (String) clusterAware.getMethod("bucketName").invoke(null, (Object[]) null); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return bucketname; + return clusterGet( "bucketName", bucketname ); } @Bean(name = "auditorAwareRef") @@ -118,6 +110,51 @@ public class Config extends AbstractCouchbaseConfiguration { return new AuditingDateTimeProvider(); } + @Override + public void configureReactiveRepositoryOperationsMapping(ReactiveRepositoryOperationsMapping baseMapping) { + try { + ReactiveCouchbaseTemplate personTemplate = myReactiveCouchbaseTemplate(myCouchbaseClientFactory("protected"),new MappingCouchbaseConverter()); + baseMapping.mapEntity(Person.class, personTemplate); // Person goes in "protected" bucket + ReactiveCouchbaseTemplate userTemplate = myReactiveCouchbaseTemplate(myCouchbaseClientFactory("mybucket"),new MappingCouchbaseConverter()); + baseMapping.mapEntity(User.class, userTemplate); // User goes in "mybucket" + // everything else goes in getBucketName() ( which is travel-sample ) + } catch (Exception e) { + throw e; + } + } + @Override + public void configureRepositoryOperationsMapping(RepositoryOperationsMapping baseMapping) { + try { + CouchbaseTemplate personTemplate = myCouchbaseTemplate(myCouchbaseClientFactory("protected"),new MappingCouchbaseConverter()); + baseMapping.mapEntity(Person.class, personTemplate); // Person goes in "protected" bucket + CouchbaseTemplate userTemplate = myCouchbaseTemplate(myCouchbaseClientFactory("mybucket"),new MappingCouchbaseConverter()); + baseMapping.mapEntity(User.class, userTemplate); // User goes in "mybucket" + // everything else goes in getBucketName() ( which is travel-sample ) + } catch (Exception e) { + throw e; + } + } + + // do not use reactiveCouchbaseTemplate for the name of this method, otherwise the value of that bean + // will be used instead of the result of this call (the client factory arg is different) + public ReactiveCouchbaseTemplate myReactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory, + MappingCouchbaseConverter mappingCouchbaseConverter) { + return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter); + } + + // do not use couchbaseTemplate for the name of this method, otherwise the value of that been + // will be used instead of the result from this call (the client factory arg is different) + public CouchbaseTemplate myCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory, + MappingCouchbaseConverter mappingCouchbaseConverter) { + return new CouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter); + } + + // do not use couchbaseClientFactory for the name of this method, otherwise the value of that bean will + // will be used instead of this call being made ( bucketname is an arg here, instead of using bucketName() ) + public CouchbaseClientFactory myCouchbaseClientFactory(String bucketName) { + return new SimpleCouchbaseClientFactory(getConnectionString(),authenticator(), bucketName ); + } + // convenience constructor for tests public MappingCouchbaseConverter mappingCouchbaseConverter() { MappingCouchbaseConverter converter = null; @@ -136,7 +173,7 @@ public class Config extends AbstractCouchbaseConfiguration { @Override @Bean(name = "couchbaseMappingConverter") public MappingCouchbaseConverter mappingCouchbaseConverter(CouchbaseMappingContext couchbaseMappingContext, - CouchbaseCustomConversions couchbaseCustomConversions) { + CustomConversions couchbaseCustomConversions) { // MappingCouchbaseConverter relies on a SimpleInformationMapper // that has an getAliasFor(info) that just returns getType().getName(). // Our CustomMappingCouchbaseConverter uses a TypeBasedCouchbaseTypeMapper that will