From e77d0a3095ef8040771194390b7a563b0e94834f Mon Sep 17 00:00:00 2001 From: mikereiche Date: Tue, 10 Aug 2021 14:36:46 -0700 Subject: [PATCH 1/3] To support jdk 16, add converters and module-info. Closes #1057. --- pom.xml | 12 ++- src/main/java/module-info.java | 46 ++++++++++ .../core/CouchbaseTemplateSupport.java | 2 +- .../ReactiveCouchbaseTemplateSupport.java | 2 +- .../convert/CouchbaseCustomConversions.java | 3 +- .../convert/MappingCouchbaseConverter.java | 19 +++-- .../core/convert/OtherConverters.java | 83 +++++++++++++++++++ .../core/mapping/CouchbaseMappingContext.java | 13 ++- .../MappingCouchbaseConverterTests.java | 14 +++- 9 files changed, 175 insertions(+), 19 deletions(-) create mode 100644 src/main/java/module-info.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java diff --git a/pom.xml b/pom.xml index 4bd644493..330b1c3e3 100644 --- a/pom.xml +++ b/pom.xml @@ -18,8 +18,8 @@ - 3.1.6 - 3.1.6 + 3.2.0 + 3.2.0 2.6.0-SNAPSHOT spring.data.couchbase @@ -273,6 +273,14 @@ org.asciidoctor asciidoctor-maven-plugin + + maven-compiler-plugin + + 9 + 9 + + + diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 000000000..db492fca3 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,46 @@ +module spring.data.couchbase { + requires spring.tx; + requires com.couchbase.client.core; + requires com.couchbase.client.java; + requires spring.data.commons; + requires spring.beans; + requires java.xml; + requires spring.context; + requires spring.core; + requires reactor.core; + requires slf4j.api; + requires spring.expression; + requires org.reactivestreams; + requires validation.api; + requires spring.aop; + requires java.desktop; + requires com.fasterxml.jackson.databind; + requires spring.context.support; + requires org.joda.time; + + // mvn integration-test > opens.log 2>&2 + // awk -F'"' '/does not/ {print $2 ";"}' opens.log | sort -u + + opens org.springframework.data.couchbase.core.mapping; + opens org.springframework.data.couchbase.core.convert.translation; + opens org.springframework.data.couchbase.core.query; + opens org.springframework.data.couchbase.domain; + opens org.springframework.data.couchbase.repository.query; + opens org.springframework.data.couchbase.util; + opens org.springframework.data.couchbase.repository; + opens org.springframework.data.couchbase.core; + + opens org.springframework.data.couchbase.config to spring.core; + opens org.springframework.data.couchbase.repository.auditing to spring.core; + + + exports org.springframework.data.couchbase.repository.config to spring.beans, spring.core; + exports org.springframework.data.couchbase.repository; + exports org.springframework.data.couchbase.config to spring.beans; + exports org.springframework.data.couchbase.repository.support to spring.beans, spring.data.commons, spring.aop; + exports org.springframework.data.couchbase.repository.auditing to spring.core, spring.beans; + exports org.springframework.data.couchbase.core.mapping.event to spring.beans, spring.core; + exports org.springframework.data.couchbase.core.mapping.id to spring.core; // comment out to reproduce NPE in validateAnnotation + exports org.springframework.data.couchbase.core.index to spring.core; + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java index bf794b9ca..4d2eaa423 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java @@ -133,7 +133,7 @@ public long getCas(final Object entity) { long cas = 0; if (versionProperty != null) { - Object casObject = (Number) accessor.getProperty(versionProperty); + Object casObject = accessor.getProperty(versionProperty); if (casObject instanceof Number) { cas = ((Number) casObject).longValue(); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java index 87f22af66..2a2c36cb7 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java @@ -140,7 +140,7 @@ public Long getCas(final Object entity) { long cas = 0; if (versionProperty != null) { - Object casObject = (Number) accessor.getProperty(versionProperty); + Object casObject = accessor.getProperty(versionProperty); if (casObject instanceof Number) { cas = ((Number) casObject).longValue(); } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java index 9f84ffa95..bfffbe514 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-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. @@ -50,6 +50,7 @@ public class CouchbaseCustomConversions extends org.springframework.data.convert 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); 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 306e8a470..bc8da6a1a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -35,6 +35,7 @@ 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.convert.EntityInstantiator; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; import org.springframework.data.couchbase.core.mapping.CouchbaseList; @@ -117,11 +118,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem private @Nullable EntityCallbacks entityCallbacks; public MappingCouchbaseConverter() { - super(new DefaultConversionService()); - - this.typeMapper = new DefaultCouchbaseTypeMapper(TYPEKEY_DEFAULT); - this.mappingContext = new CouchbaseMappingContext(); - this.spELContext = new SpELContext(CouchbaseDocumentPropertyAccessor.INSTANCE); + this(new CouchbaseMappingContext(), null); } /** @@ -131,7 +128,7 @@ public MappingCouchbaseConverter() { */ public MappingCouchbaseConverter( final MappingContext, CouchbasePersistentProperty> mappingContext) { - this(mappingContext, TYPEKEY_DEFAULT); + this(mappingContext, null); } /** @@ -145,8 +142,14 @@ public MappingCouchbaseConverter( final MappingContext, 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); } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java b/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java new file mode 100644 index 000000000..b6ab67e3c --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java @@ -0,0 +1,83 @@ +/* + * Copyright 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.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> getConvertersToRegister() { + List> converters = new ArrayList>(); + + converters.add(UuidToString.INSTANCE); + converters.add(StringToUuid.INSTANCE); + converters.add(BigIntegerToString.INSTANCE); + converters.add(StringToBigInteger.INSTANCE); + + return converters; + } + + @WritingConverter public enum UuidToString implements Converter + { + INSTANCE; + + @Override + public String convert(UUID source) { + return source == null ? null : source.toString(); + }} + + @ReadingConverter public enum StringToUuid implements Converter{INSTANCE; + + @Override + public UUID convert(String source) { + return source == null ? null : UUID.fromString(source); + }} + + @WritingConverter public enum BigIntegerToString implements Converter{INSTANCE; + + @Override + public String convert(BigInteger source) { + return source == null ? null : source.toString(); + }} + + @ReadingConverter public enum StringToBigInteger implements Converter{INSTANCE; + + @Override + public BigInteger convert(String source) { + return source == null ? null : new BigInteger(source); + } +}} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java index 6421dcd60..c6696837d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -16,6 +16,7 @@ package org.springframework.data.couchbase.core.mapping; +import java.lang.reflect.InaccessibleObjectException; import java.util.Optional; import org.springframework.beans.BeansException; @@ -23,6 +24,7 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator; +import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.context.MappingContextEvent; import org.springframework.data.mapping.model.FieldNamingStrategy; @@ -131,12 +133,17 @@ public void setAutoIndexCreation(boolean autoCreateIndexes) { /** * override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has * already been cached - * + * * @param typeInformation - entity type */ @Override protected Optional> addPersistentEntity(TypeInformation typeInformation) { - Optional> entity = super.addPersistentEntity(typeInformation); + Optional> entity = null; + try { + entity = super.addPersistentEntity(typeInformation); + } catch (InaccessibleObjectException ioe) { + throw new MappingException("due to InaccessibleObjectException", ioe); + } if (this.eventPublisher != null && entity.isPresent()) { if (this.indexCreator != null) { 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 b754514db..2db2b2d60 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 @@ -29,6 +29,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.TypeAlias; +import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; @@ -421,8 +422,10 @@ void writesAndReadsCustomConvertedClass() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); CouchbaseDocument converted = new CouchbaseDocument(); @@ -469,8 +472,11 @@ void writesAndReadsCustomFieldsConvertedClass() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); + CouchbaseDocument converted = new CouchbaseDocument(); @@ -517,8 +523,10 @@ void writesAndReadsClassContainingCustomConvertedObjects() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); CouchbaseDocument converted = new CouchbaseDocument(); From 587be0660a3c66acec3cd056588110e0db671fdf Mon Sep 17 00:00:00 2001 From: mikereiche Date: Tue, 10 Aug 2021 14:36:46 -0700 Subject: [PATCH 2/3] To support jdk 16, add converters and module-info. Closes #1057. --- pom.xml | 11 ++- src/main/java/module-info.java | 46 ++++++++++ .../core/CouchbaseTemplateSupport.java | 2 +- .../ReactiveCouchbaseTemplateSupport.java | 2 +- .../convert/CouchbaseCustomConversions.java | 3 +- .../convert/MappingCouchbaseConverter.java | 19 +++-- .../core/convert/OtherConverters.java | 83 +++++++++++++++++++ .../core/mapping/CouchbaseMappingContext.java | 13 ++- .../MappingCouchbaseConverterTests.java | 14 +++- .../data/couchbase/domain/FluxTest.java | 21 +++++ 10 files changed, 195 insertions(+), 19 deletions(-) create mode 100644 src/main/java/module-info.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java diff --git a/pom.xml b/pom.xml index 4bd644493..bbc40affd 100644 --- a/pom.xml +++ b/pom.xml @@ -18,8 +18,8 @@ - 3.1.6 - 3.1.6 + 3.2.0 + 3.2.0 2.6.0-SNAPSHOT spring.data.couchbase @@ -273,6 +273,13 @@ org.asciidoctor asciidoctor-maven-plugin + + maven-compiler-plugin + + 16 + 16 + + diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 000000000..db492fca3 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,46 @@ +module spring.data.couchbase { + requires spring.tx; + requires com.couchbase.client.core; + requires com.couchbase.client.java; + requires spring.data.commons; + requires spring.beans; + requires java.xml; + requires spring.context; + requires spring.core; + requires reactor.core; + requires slf4j.api; + requires spring.expression; + requires org.reactivestreams; + requires validation.api; + requires spring.aop; + requires java.desktop; + requires com.fasterxml.jackson.databind; + requires spring.context.support; + requires org.joda.time; + + // mvn integration-test > opens.log 2>&2 + // awk -F'"' '/does not/ {print $2 ";"}' opens.log | sort -u + + opens org.springframework.data.couchbase.core.mapping; + opens org.springframework.data.couchbase.core.convert.translation; + opens org.springframework.data.couchbase.core.query; + opens org.springframework.data.couchbase.domain; + opens org.springframework.data.couchbase.repository.query; + opens org.springframework.data.couchbase.util; + opens org.springframework.data.couchbase.repository; + opens org.springframework.data.couchbase.core; + + opens org.springframework.data.couchbase.config to spring.core; + opens org.springframework.data.couchbase.repository.auditing to spring.core; + + + exports org.springframework.data.couchbase.repository.config to spring.beans, spring.core; + exports org.springframework.data.couchbase.repository; + exports org.springframework.data.couchbase.config to spring.beans; + exports org.springframework.data.couchbase.repository.support to spring.beans, spring.data.commons, spring.aop; + exports org.springframework.data.couchbase.repository.auditing to spring.core, spring.beans; + exports org.springframework.data.couchbase.core.mapping.event to spring.beans, spring.core; + exports org.springframework.data.couchbase.core.mapping.id to spring.core; // comment out to reproduce NPE in validateAnnotation + exports org.springframework.data.couchbase.core.index to spring.core; + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java index bf794b9ca..4d2eaa423 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java @@ -133,7 +133,7 @@ public long getCas(final Object entity) { long cas = 0; if (versionProperty != null) { - Object casObject = (Number) accessor.getProperty(versionProperty); + Object casObject = accessor.getProperty(versionProperty); if (casObject instanceof Number) { cas = ((Number) casObject).longValue(); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java index 87f22af66..2a2c36cb7 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java @@ -140,7 +140,7 @@ public Long getCas(final Object entity) { long cas = 0; if (versionProperty != null) { - Object casObject = (Number) accessor.getProperty(versionProperty); + Object casObject = accessor.getProperty(versionProperty); if (casObject instanceof Number) { cas = ((Number) casObject).longValue(); } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java index 9f84ffa95..bfffbe514 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-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. @@ -50,6 +50,7 @@ public class CouchbaseCustomConversions extends org.springframework.data.convert 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); 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 306e8a470..bc8da6a1a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -35,6 +35,7 @@ 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.convert.EntityInstantiator; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; import org.springframework.data.couchbase.core.mapping.CouchbaseList; @@ -117,11 +118,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem private @Nullable EntityCallbacks entityCallbacks; public MappingCouchbaseConverter() { - super(new DefaultConversionService()); - - this.typeMapper = new DefaultCouchbaseTypeMapper(TYPEKEY_DEFAULT); - this.mappingContext = new CouchbaseMappingContext(); - this.spELContext = new SpELContext(CouchbaseDocumentPropertyAccessor.INSTANCE); + this(new CouchbaseMappingContext(), null); } /** @@ -131,7 +128,7 @@ public MappingCouchbaseConverter() { */ public MappingCouchbaseConverter( final MappingContext, CouchbasePersistentProperty> mappingContext) { - this(mappingContext, TYPEKEY_DEFAULT); + this(mappingContext, null); } /** @@ -145,8 +142,14 @@ public MappingCouchbaseConverter( final MappingContext, 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); } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java b/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java new file mode 100644 index 000000000..b6ab67e3c --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java @@ -0,0 +1,83 @@ +/* + * Copyright 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.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> getConvertersToRegister() { + List> converters = new ArrayList>(); + + converters.add(UuidToString.INSTANCE); + converters.add(StringToUuid.INSTANCE); + converters.add(BigIntegerToString.INSTANCE); + converters.add(StringToBigInteger.INSTANCE); + + return converters; + } + + @WritingConverter public enum UuidToString implements Converter + { + INSTANCE; + + @Override + public String convert(UUID source) { + return source == null ? null : source.toString(); + }} + + @ReadingConverter public enum StringToUuid implements Converter{INSTANCE; + + @Override + public UUID convert(String source) { + return source == null ? null : UUID.fromString(source); + }} + + @WritingConverter public enum BigIntegerToString implements Converter{INSTANCE; + + @Override + public String convert(BigInteger source) { + return source == null ? null : source.toString(); + }} + + @ReadingConverter public enum StringToBigInteger implements Converter{INSTANCE; + + @Override + public BigInteger convert(String source) { + return source == null ? null : new BigInteger(source); + } +}} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java index 6421dcd60..c6696837d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -16,6 +16,7 @@ package org.springframework.data.couchbase.core.mapping; +import java.lang.reflect.InaccessibleObjectException; import java.util.Optional; import org.springframework.beans.BeansException; @@ -23,6 +24,7 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator; +import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.context.MappingContextEvent; import org.springframework.data.mapping.model.FieldNamingStrategy; @@ -131,12 +133,17 @@ public void setAutoIndexCreation(boolean autoCreateIndexes) { /** * override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has * already been cached - * + * * @param typeInformation - entity type */ @Override protected Optional> addPersistentEntity(TypeInformation typeInformation) { - Optional> entity = super.addPersistentEntity(typeInformation); + Optional> entity = null; + try { + entity = super.addPersistentEntity(typeInformation); + } catch (InaccessibleObjectException ioe) { + throw new MappingException("due to InaccessibleObjectException", ioe); + } if (this.eventPublisher != null && entity.isPresent()) { if (this.indexCreator != null) { 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 b754514db..2db2b2d60 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 @@ -29,6 +29,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.TypeAlias; +import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; @@ -421,8 +422,10 @@ void writesAndReadsCustomConvertedClass() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); CouchbaseDocument converted = new CouchbaseDocument(); @@ -469,8 +472,11 @@ void writesAndReadsCustomFieldsConvertedClass() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); + CouchbaseDocument converted = new CouchbaseDocument(); @@ -517,8 +523,10 @@ void writesAndReadsClassContainingCustomConvertedObjects() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); CouchbaseDocument converted = new CouchbaseDocument(); diff --git a/src/test/java/org/springframework/data/couchbase/domain/FluxTest.java b/src/test/java/org/springframework/data/couchbase/domain/FluxTest.java index f6ca822a4..634f4c9a7 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/FluxTest.java +++ b/src/test/java/org/springframework/data/couchbase/domain/FluxTest.java @@ -14,6 +14,7 @@ import org.springframework.data.couchbase.util.Capabilities; import org.springframework.data.couchbase.util.ClusterType; import org.springframework.data.couchbase.util.IgnoreWhen; +import org.springframework.data.util.Pair; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.ParallelFlux; @@ -130,6 +131,26 @@ public void cbse() { assertEquals(0, airports.size(), "should have been all deleted"); } + @Test + @IgnoreWhen(missesCapabilities = { Capabilities.QUERY }, clusterTypes = ClusterType.MOCKED) + public void pairIdAndResult() { + LinkedList list = new LinkedList<>(); + Airport a = new Airport(UUID.randomUUID().toString(), "iata", "lowp"); + for (int i = 0; i < 5; i++) { + list.add(a.withId(UUID.randomUUID().toString())); + } + Flux af = Flux.fromIterable(list).concatMap((entity) -> airportRepository.save(entity)); + List saved = af.collectList().block(); + System.out.println("results.size() : " + saved.size()); + Flux>> pairFlux = Flux.fromIterable(list) + .map((airport) -> Pair.of(airport.getId(), airportRepository.findById(airport.getId()))); + List>> airportPairs = pairFlux.collectList().block(); + for (Pair> airportPair : airportPairs) { + System.out.println("id: " + airportPair.getFirst() + " airport: " + airportPair.getSecond().block()); + } + + } + @Test public void flatMapCB() throws Exception { System.out.println("Start flatMapCB"); From 26c7d8923a5f122af4399be57d818e2328bf53b1 Mon Sep 17 00:00:00 2001 From: mikereiche Date: Tue, 10 Aug 2021 14:36:46 -0700 Subject: [PATCH 3/3] To support jdk 16, add converters and module-info. Closes #1057. --- pom.xml | 4 +- .../core/CouchbaseTemplateSupport.java | 2 +- .../ReactiveCouchbaseTemplateSupport.java | 2 +- .../convert/CouchbaseCustomConversions.java | 3 +- .../convert/MappingCouchbaseConverter.java | 19 +++-- .../core/convert/OtherConverters.java | 83 +++++++++++++++++++ ...CouchbasePersistentEntityIndexCreator.java | 9 +- ...ouchbasePersistentEntityIndexResolver.java | 13 ++- .../core/mapping/CouchbaseMappingContext.java | 13 ++- .../CrudMethodMetadataPostProcessor.java | 10 +-- src/main/resources/notice.txt | 3 +- .../MappingCouchbaseConverterTests.java | 14 +++- .../data/couchbase/domain/Airline.java | 1 + .../data/couchbase/domain/FluxTest.java | 21 +++++ .../data/couchbase/domain/User.java | 9 ++ .../couchbase/domain/UserColRepository.java | 4 +- .../data/couchbase/domain/UserRepository.java | 10 ++- ...ositoryAutoQueryIndexIntegrationTests.java | 8 ++ ...chbaseRepositoryQueryIntegrationTests.java | 27 +++++- ...sitoryQueryCollectionIntegrationTests.java | 19 ++++- 20 files changed, 233 insertions(+), 41 deletions(-) create mode 100644 src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java diff --git a/pom.xml b/pom.xml index 4bd644493..d895665e2 100644 --- a/pom.xml +++ b/pom.xml @@ -18,8 +18,8 @@ - 3.1.6 - 3.1.6 + 3.2.0 + 3.2.0 2.6.0-SNAPSHOT spring.data.couchbase diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java index bf794b9ca..4d2eaa423 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java @@ -133,7 +133,7 @@ public long getCas(final Object entity) { long cas = 0; if (versionProperty != null) { - Object casObject = (Number) accessor.getProperty(versionProperty); + Object casObject = accessor.getProperty(versionProperty); if (casObject instanceof Number) { cas = ((Number) casObject).longValue(); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java index 87f22af66..2a2c36cb7 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java @@ -140,7 +140,7 @@ public Long getCas(final Object entity) { long cas = 0; if (versionProperty != null) { - Object casObject = (Number) accessor.getProperty(versionProperty); + Object casObject = accessor.getProperty(versionProperty); if (casObject instanceof Number) { cas = ((Number) casObject).longValue(); } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java index 9f84ffa95..bfffbe514 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-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. @@ -50,6 +50,7 @@ public class CouchbaseCustomConversions extends org.springframework.data.convert 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); 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 306e8a470..bc8da6a1a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -35,6 +35,7 @@ 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.convert.EntityInstantiator; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; import org.springframework.data.couchbase.core.mapping.CouchbaseList; @@ -117,11 +118,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem private @Nullable EntityCallbacks entityCallbacks; public MappingCouchbaseConverter() { - super(new DefaultConversionService()); - - this.typeMapper = new DefaultCouchbaseTypeMapper(TYPEKEY_DEFAULT); - this.mappingContext = new CouchbaseMappingContext(); - this.spELContext = new SpELContext(CouchbaseDocumentPropertyAccessor.INSTANCE); + this(new CouchbaseMappingContext(), null); } /** @@ -131,7 +128,7 @@ public MappingCouchbaseConverter() { */ public MappingCouchbaseConverter( final MappingContext, CouchbasePersistentProperty> mappingContext) { - this(mappingContext, TYPEKEY_DEFAULT); + this(mappingContext, null); } /** @@ -145,8 +142,14 @@ public MappingCouchbaseConverter( final MappingContext, 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); } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java b/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java new file mode 100644 index 000000000..b6ab67e3c --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java @@ -0,0 +1,83 @@ +/* + * Copyright 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.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> getConvertersToRegister() { + List> converters = new ArrayList>(); + + converters.add(UuidToString.INSTANCE); + converters.add(StringToUuid.INSTANCE); + converters.add(BigIntegerToString.INSTANCE); + converters.add(StringToBigInteger.INSTANCE); + + return converters; + } + + @WritingConverter public enum UuidToString implements Converter + { + INSTANCE; + + @Override + public String convert(UUID source) { + return source == null ? null : source.toString(); + }} + + @ReadingConverter public enum StringToUuid implements Converter{INSTANCE; + + @Override + public UUID convert(String source) { + return source == null ? null : UUID.fromString(source); + }} + + @WritingConverter public enum BigIntegerToString implements Converter{INSTANCE; + + @Override + public String convert(BigInteger source) { + return source == null ? null : source.toString(); + }} + + @ReadingConverter public enum StringToBigInteger implements Converter{INSTANCE; + + @Override + public BigInteger convert(String source) { + return source == null ? null : new BigInteger(source); + } +}} diff --git a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java index 39ca60b7f..f753bc3fd 100644 --- a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java +++ b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -34,6 +34,10 @@ import com.couchbase.client.core.error.IndexExistsException; import com.couchbase.client.java.Cluster; +/** + * @author Michael Nitschinger + * @author Michael Reiche + */ public class CouchbasePersistentEntityIndexCreator implements ApplicationListener> { private static final Logger LOGGER = LoggerFactory.getLogger(CouchbasePersistentEntityIndexCreator.class); @@ -95,7 +99,8 @@ private void checkForAndCreateIndexes(final CouchbasePersistentEntity entity) private void createIndex(final IndexDefinitionHolder indexToCreate) { Cluster cluster = couchbaseOperations.getCouchbaseClientFactory().getCluster(); - StringBuilder statement = new StringBuilder("CREATE INDEX ").append(indexToCreate.getIndexName()).append(" ON `") + StringBuilder statement = new StringBuilder("CREATE INDEX `") + .append(indexToCreate.getIndexName()).append("` ON `") .append(couchbaseOperations.getBucketName()).append("` (") .append(String.join(",", indexToCreate.getIndexFields())).append(")"); diff --git a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java index 4ef36a4de..a92369e6c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java +++ b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -32,6 +32,10 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; +/** + * @author Michael Nitschinger + * @author Michael Reiche + */ public class CouchbasePersistentEntityIndexResolver implements QueryIndexResolver { private final MappingContext, CouchbasePersistentProperty> mappingContext; @@ -103,7 +107,8 @@ protected IndexDefinitionHolder createFieldQueryIndexDefinition(final CouchbaseP 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; + String indexName = "idx_" + StringUtils.uncapitalize(entity.getType().getSimpleName()) + "_" + + fieldName.replace(".", "_"); return new IndexDefinitionHolder(fields, indexName, getPredicate(entityInfo)); } @@ -126,8 +131,8 @@ protected List createCompositeQueryIndexDefinitions(final return indexAnnotations.stream().map(ann -> { List fields = Arrays.asList(ann.fields()); - String fieldsIndexName = String.join("_", fields).toLowerCase().replace(" ", "").replace("asc", "") - .replace("desc", ""); + 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); diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java index 6421dcd60..c6696837d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -16,6 +16,7 @@ package org.springframework.data.couchbase.core.mapping; +import java.lang.reflect.InaccessibleObjectException; import java.util.Optional; import org.springframework.beans.BeansException; @@ -23,6 +24,7 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator; +import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.context.MappingContextEvent; import org.springframework.data.mapping.model.FieldNamingStrategy; @@ -131,12 +133,17 @@ public void setAutoIndexCreation(boolean autoCreateIndexes) { /** * override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has * already been cached - * + * * @param typeInformation - entity type */ @Override protected Optional> addPersistentEntity(TypeInformation typeInformation) { - Optional> entity = super.addPersistentEntity(typeInformation); + Optional> entity = null; + try { + entity = super.addPersistentEntity(typeInformation); + } catch (InaccessibleObjectException ioe) { + throw new MappingException("due to InaccessibleObjectException", ioe); + } if (this.eventPublisher != null && entity.isPresent()) { if (this.indexCreator != null) { diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadataPostProcessor.java b/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadataPostProcessor.java index 836db257f..0e5dd19bf 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadataPostProcessor.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadataPostProcessor.java @@ -145,7 +145,6 @@ public Object invoke(MethodInvocation invocation) throws Throwable { currentInvocation.set(invocation); try { - CrudMethodMetadata metadata = (CrudMethodMetadata) TransactionSynchronizationManager.getResource(method); if (metadata != null) { @@ -156,7 +155,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { if (methodMetadata == null) { - methodMetadata = new DefaultCrudMethodMetadata(method, repositoryInformation); + methodMetadata = new DefaultCrudMethodMetadata(method); CrudMethodMetadata tmp = metadataCache.putIfAbsent(method, methodMetadata); if (tmp != null) { @@ -187,7 +186,6 @@ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { private final Method method; private final ScanConsistency scanConsistency; - private final RepositoryInformation repositoryInformation; private final String scope; private final String collection; @@ -198,10 +196,9 @@ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { * * @param method must not be {@literal null}. */ - DefaultCrudMethodMetadata(Method method, RepositoryInformation repositoryInformation) { + DefaultCrudMethodMetadata(Method method) { Assert.notNull(method, "Method must not be null!"); this.method = method; - this.repositoryInformation = repositoryInformation; String n = method.getName(); // internal methods if (n.equals("getEntityInformation") || n.equals("getOperations") || n.equals("withOptions") @@ -212,8 +209,7 @@ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { return; } - AnnotatedElement[] annotated = new AnnotatedElement[] { method, method.getDeclaringClass(), - repositoryInformation.getRepositoryInterface(), repositoryInformation.getDomainType() }; + AnnotatedElement[] annotated = new AnnotatedElement[] { method, method.getDeclaringClass()}; this.scanConsistency = OptionsBuilder.annotation(ScanConsistency.class, "query", QueryScanConsistency.NOT_BOUNDED, annotated); this.scope = OptionsBuilder.annotationString(Scope.class, CollectionIdentifier.DEFAULT_SCOPE, annotated); diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 5d6f8f853..9f33c40cb 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Couchbase 4.3 M1 (2021.1.0) +Spring Data Couchbase 4.3 M2 (2021.1.0) Copyright (c) [2013-2019] Couchbase / Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -27,4 +27,5 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + 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 b754514db..2db2b2d60 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 @@ -29,6 +29,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.TypeAlias; +import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; @@ -421,8 +422,10 @@ void writesAndReadsCustomConvertedClass() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); CouchbaseDocument converted = new CouchbaseDocument(); @@ -469,8 +472,11 @@ void writesAndReadsCustomFieldsConvertedClass() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); + CouchbaseDocument converted = new CouchbaseDocument(); @@ -517,8 +523,10 @@ void writesAndReadsClassContainingCustomConvertedObjects() { List converters = new ArrayList<>(); converters.add(BigDecimalToStringConverter.INSTANCE); converters.add(StringToBigDecimalConverter.INSTANCE); - converter.setCustomConversions(new CouchbaseCustomConversions(converters)); + CustomConversions customConversions = new CouchbaseCustomConversions(converters); + converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); + ((CouchbaseMappingContext) converter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); CouchbaseDocument converted = new CouchbaseDocument(); diff --git a/src/test/java/org/springframework/data/couchbase/domain/Airline.java b/src/test/java/org/springframework/data/couchbase/domain/Airline.java index 402412013..157669558 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/Airline.java +++ b/src/test/java/org/springframework/data/couchbase/domain/Airline.java @@ -23,6 +23,7 @@ @Document @CompositeQueryIndex(fields = { "id", "name desc" }) +@CompositeQueryIndex(fields = { "id.something", "name desc" }) public class Airline extends ComparableEntity { @Id String id; diff --git a/src/test/java/org/springframework/data/couchbase/domain/FluxTest.java b/src/test/java/org/springframework/data/couchbase/domain/FluxTest.java index f6ca822a4..634f4c9a7 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/FluxTest.java +++ b/src/test/java/org/springframework/data/couchbase/domain/FluxTest.java @@ -14,6 +14,7 @@ import org.springframework.data.couchbase.util.Capabilities; import org.springframework.data.couchbase.util.ClusterType; import org.springframework.data.couchbase.util.IgnoreWhen; +import org.springframework.data.util.Pair; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.ParallelFlux; @@ -130,6 +131,26 @@ public void cbse() { assertEquals(0, airports.size(), "should have been all deleted"); } + @Test + @IgnoreWhen(missesCapabilities = { Capabilities.QUERY }, clusterTypes = ClusterType.MOCKED) + public void pairIdAndResult() { + LinkedList list = new LinkedList<>(); + Airport a = new Airport(UUID.randomUUID().toString(), "iata", "lowp"); + for (int i = 0; i < 5; i++) { + list.add(a.withId(UUID.randomUUID().toString())); + } + Flux af = Flux.fromIterable(list).concatMap((entity) -> airportRepository.save(entity)); + List saved = af.collectList().block(); + System.out.println("results.size() : " + saved.size()); + Flux>> pairFlux = Flux.fromIterable(list) + .map((airport) -> Pair.of(airport.getId(), airportRepository.findById(airport.getId()))); + List>> airportPairs = pairFlux.collectList().block(); + for (Pair> airportPair : airportPairs) { + System.out.println("id: " + airportPair.getFirst() + " airport: " + airportPair.getSecond().block()); + } + + } + @Test public void flatMapCB() throws Exception { System.out.println("Start flatMapCB"); diff --git a/src/test/java/org/springframework/data/couchbase/domain/User.java b/src/test/java/org/springframework/data/couchbase/domain/User.java index 6fa53f7b2..ac2b92d63 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/User.java +++ b/src/test/java/org/springframework/data/couchbase/domain/User.java @@ -24,6 +24,7 @@ import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.Version; import org.springframework.data.couchbase.core.mapping.Document; @@ -41,6 +42,8 @@ public class User extends ComparableEntity { @Id private String id; private String firstname; private String lastname; + @Transient + private String transientInfo; @CreatedBy private String createdBy; @CreatedDate private long createdDate; @LastModifiedBy private String lastModifiedBy; @@ -94,4 +97,10 @@ public int hashCode() { return Objects.hash(id, firstname, lastname); } + public String getTransientInfo(){ + return transientInfo; + } + public void setTransientInfo(String something) { + transientInfo = something; + } } diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserColRepository.java b/src/test/java/org/springframework/data/couchbase/domain/UserColRepository.java index e1bbaa370..412a5755d 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/UserColRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/UserColRepository.java @@ -38,7 +38,9 @@ @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) public interface UserColRepository extends CouchbaseRepository, DynamicProxyable { - S save(S var1); + // CouchbaseRepositoryQueryCollectionIntegrationTests.testScopeCollectionAnnotationSwap() relies on this + // being commented out. + // S save(S var1); List findByFirstname(String firstname); diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java b/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java index 18b334d06..229ae00c0 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * 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. @@ -17,7 +17,9 @@ package org.springframework.data.couchbase.domain; import java.util.List; +import java.util.stream.Stream; +import com.couchbase.client.java.query.QueryScanConsistency; import org.springframework.data.couchbase.repository.CouchbaseRepository; import org.springframework.data.couchbase.repository.Query; import org.springframework.data.couchbase.repository.ScanConsistency; @@ -25,7 +27,6 @@ import org.springframework.stereotype.Repository; import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.query.QueryScanConsistency; /** * User Repository for tests @@ -34,11 +35,13 @@ * @author Michael Reiche */ @Repository -@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) +@ScanConsistency(query=QueryScanConsistency.REQUEST_PLUS) public interface UserRepository extends CouchbaseRepository { List findByFirstname(String firstname); + Stream findByLastname(String lastname); + List findByFirstnameIn(String... firstnames); List findByFirstnameIn(JsonArray firstnames); @@ -54,5 +57,4 @@ public interface UserRepository extends CouchbaseRepository { List findByIdIsNotNullAndFirstnameEquals(String firstname); List findByVersionEqualsAndFirstnameEquals(Long version, String firstname); - } diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryAutoQueryIndexIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryAutoQueryIndexIntegrationTests.java index 42bcd73be..375dc3555 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryAutoQueryIndexIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryAutoQueryIndexIntegrationTests.java @@ -62,6 +62,14 @@ void createsCompositeIndex() { assertTrue(foundIndex.get().condition().get().contains("_class")); } + @Test + void createsCompositeIndexWithPath() { + Optional foundIndex = cluster.queryIndexes().getAllIndexes(bucketName()).stream() + .filter(i -> i.name().equals("idx_airline_id_something_name")).findFirst(); + + assertTrue(foundIndex.isPresent()); + assertTrue(foundIndex.get().condition().get().contains("_class")); + } @Configuration @EnableCouchbaseRepositories("org.springframework.data.couchbase") static class Config extends AbstractCouchbaseConfiguration { diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java index 5b5eda212..cffeeaee2 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -35,6 +35,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.Optional; import java.util.stream.Collectors; import org.junit.jupiter.api.BeforeEach; @@ -354,11 +355,20 @@ public void saveNotBounded() { assertNull(airport2, "airport2 should have likely been null at least once"); } + @Test + public void testTransient() { + User user = new User("1", "Dave", "Wilson"); + user.setTransientInfo("something"); + userRepository.save(user); + Optional foundUser = userRepository.findById(user.getId()); + assertEquals(null, foundUser.get().getTransientInfo()); + userRepository.delete(user); + } + @Test public void testCas() { User user = new User("1", "Dave", "Wilson"); userRepository.save(user); - userRepository.findByFirstname("Dave"); user.setVersion(user.getVersion() - 1); assertThrows(DataIntegrityViolationException.class, () -> userRepository.save(user)); user.setVersion(0); @@ -366,6 +376,21 @@ public void testCas() { userRepository.delete(user); } + @Test + public void testStreamQuery() { + User user1 = new User("1", "Dave", "Wilson"); + User user2 = new User("2", "Brian", "Wilson"); + + userRepository.save(user1); + userRepository.save(user2); + List users = userRepository.findByLastname("Wilson").collect(Collectors.toList()); + assertEquals(2,users.size()); + assertTrue(users.contains(user1)); + assertTrue(users.contains(user2)); + userRepository.delete(user1); + userRepository.delete(user2); + } + @Test public void testExpiryAnnotation() { UserAnnotated user = new UserAnnotated("1", "Dave", "Wilson"); diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQueryCollectionIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQueryCollectionIntegrationTests.java index f522671e8..82d24e8d5 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQueryCollectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQueryCollectionIntegrationTests.java @@ -15,6 +15,7 @@ */ package org.springframework.data.couchbase.repository.query; +import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_SCOPE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -178,7 +179,7 @@ public void testScopeCollectionAnnotation() { // scope List found = userColRepository.withCollection(otherCollection).findByFirstname(user.getFirstname()); assertEquals(saved, found.get(0), "should have found what was saved"); - List notfound = userColRepository.withScope(CollectionIdentifier.DEFAULT_SCOPE) + List notfound = userColRepository.withScope(DEFAULT_SCOPE) .withCollection(CollectionIdentifier.DEFAULT_COLLECTION).findByFirstname(user.getFirstname()); assertEquals(0, notfound.size(), "should not have found what was saved"); } finally { @@ -188,6 +189,20 @@ public void testScopeCollectionAnnotation() { } } + @Test + public void testScopeCollectionAnnotationSwap() { + // UserCol annotation scope is other_scope, collection is other_collection + // airportRepository relies on Config.setScopeName(scopeName) ("my_scope") from CollectionAwareIntegrationTests. + // using airportRepository without specified a collection should fail. + // This test ensures that airportRepository.save(airport) doesn't get the + // collection from CrudMethodMetadata of UserCol.save() + UserCol userCol = new UserCol("1", "Dave", "Wilson"); + Airport airport = new Airport("3", "myIata", "myIcao"); + UserCol savedCol = userColRepository.save(userCol); // uses UserCol annotation scope, populates CrudMethodMetadata + userColRepository.delete(userCol); // uses UserCol annotation scope, populates CrudMethodMetadata + assertThrows(IllegalStateException.class, () -> airportRepository.save(airport)); + } + // template default scope is my_scope // UserCol annotation scope is other_scope @Test @@ -198,7 +213,7 @@ public void testScopeCollectionRepoWith() { List found = userColRepository.withScope(scopeName).withCollection(collectionName) .findByFirstname(user.getFirstname()); assertEquals(saved, found.get(0), "should have found what was saved"); - List notfound = userColRepository.withScope(CollectionIdentifier.DEFAULT_SCOPE) + List notfound = userColRepository.withScope(DEFAULT_SCOPE) .withCollection(CollectionIdentifier.DEFAULT_COLLECTION).findByFirstname(user.getFirstname()); assertEquals(0, notfound.size(), "should not have found what was saved"); userColRepository.withScope(scopeName).withCollection(collectionName).delete(user);