diff --git a/generators/cleanup.js b/generators/cleanup.js index ccabed28d92a..2b155bad368c 100644 --- a/generators/cleanup.js +++ b/generators/cleanup.js @@ -459,6 +459,13 @@ function cleanupOldServerFiles(generator, javaDir, testDir, mainResourceDir, tes if (generator.searchEngine === 'elasticsearch') { generator.removeFile(`${testDir}config/ElasticsearchTestConfiguration.java`); } + if (generator.databaseType === 'couchbase') { + generator.removeFile(`${javaDir}repository/N1qlCouchbaseRepository.java`); + generator.removeFile(`${testDir}config/DatabaseConfigurationIT.java`); + if (generator.searchEngine !== 'couchbase') { + generator.removeFile(`${javaDir}repository/CustomN1qlCouchbaseRepository.java`); + } + } } if (generator.isJhipsterVersionLessThan('7.0.0-beta.1')) { generator.removeFile(`${javaDir}config/CloudDatabaseConfiguration.java`); diff --git a/generators/entity-server/templates/src/main/java/package/domain/Entity.java.ejs b/generators/entity-server/templates/src/main/java/package/domain/Entity.java.ejs index 39d1f719a4dc..14c4a5c14bfa 100644 --- a/generators/entity-server/templates/src/main/java/package/domain/Entity.java.ejs +++ b/generators/entity-server/templates/src/main/java/package/domain/Entity.java.ejs @@ -71,7 +71,7 @@ import org.springframework.data.couchbase.core.mapping.Document; import org.springframework.data.couchbase.core.mapping.id.GeneratedValue; import org.springframework.data.couchbase.core.mapping.id.IdPrefix; <%_ } _%> -import com.couchbase.client.java.repository.annotation.Field; +import org.springframework.data.couchbase.core.mapping.Field; <%_ if (hasRelationship) { _%> import org.springframework.data.couchbase.core.query.FetchType; import org.springframework.data.couchbase.core.query.N1qlJoin; diff --git a/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs b/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs index 4e11663e9f3a..e20c57e11433 100644 --- a/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs +++ b/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs @@ -29,19 +29,24 @@ import org.springframework.data.jpa.repository.*; <%_ if (relationshipsContainEagerLoad) { _%> import org.springframework.data.repository.query.Param; <%_ } _%> -<%_ } _%> -<%_ if (databaseType === 'mongodb') { _%> +<%_ } else if (databaseType === 'mongodb') { _%> import org.springframework.data.mongodb.repository.Query; import org.springframework.data.mongodb.repository.MongoRepository; -<%_ } _%> -<%_ if (databaseType === 'neo4j') { _%> +<%_ } else if (databaseType === 'neo4j') { _%> import org.springframework.data.neo4j.repository.Neo4jRepository; +<%_ } else if (databaseType === 'couchbase') { _%> +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.couchbase.repository.CouchbaseRepository; +import org.springframework.data.couchbase.repository.ScanConsistency; <%_ } _%> <%_ if (searchEngine === 'couchbase') { _%> import <%= packageName %>.repository.search.SearchCouchbaseRepository; <%_ } _%> <%_ if (databaseType === 'couchbase' && relationshipsContainEagerLoad === true) { _%> -import org.springframework.data.couchbase.core.query.Query; +import org.springframework.data.couchbase.repository.Query; <%_ } _%> <%_ if (databaseType === 'cassandra') { _%> import org.springframework.data.cassandra.repository.CassandraRepository; @@ -56,7 +61,7 @@ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'co } } _%> - <%_ if (importList) { _%> + <%_ if (importList || databaseType === 'couchbase') { _%> import java.util.List; <%_ } _%> <%_ if (relationshipsContainEagerLoad) { _%> @@ -75,7 +80,7 @@ import java.util.UUID; @SuppressWarnings("unused") <%_ } _%> @Repository -public interface <%= entityClass %>Repository extends <% if (databaseType === 'sql') { %>JpaRepository<% } %><% if (databaseType === 'mongodb') { %>MongoRepository<% } %><% if (databaseType === 'neo4j') { %>Neo4jRepository<% } %><% if (databaseType === 'cassandra') { %>CassandraRepository<% } %><% if (databaseType === 'couchbase') { %>N1qlCouchbaseRepository<% } %><<%= asEntity(entityClass) %>, <%= primaryKey.type %>><% if (jpaMetamodelFiltering) { %>, JpaSpecificationExecutor<<%= asEntity(entityClass) %>><% } %><% if (searchEngine === 'couchbase') { %>, SearchCouchbaseRepository<<%= asEntity(entityClass) %>, <%= primaryKey.type %>><% } %> { +public interface <%= entityClass %>Repository extends <% if (databaseType === 'sql') { %>JpaRepository<% } %><% if (databaseType === 'mongodb') { %>MongoRepository<% } %><% if (databaseType === 'neo4j') { %>Neo4jRepository<% } %><% if (databaseType === 'cassandra') { %>CassandraRepository<% } %><% if (databaseType === 'couchbase') { %>CouchbaseRepository<% } %><<%= asEntity(entityClass) %>, <%= primaryKey.type %>><% if (jpaMetamodelFiltering) { %>, JpaSpecificationExecutor<<%= asEntity(entityClass) %>><% } %><% if (searchEngine === 'couchbase') { %>, SearchCouchbaseRepository<<%= asEntity(entityClass) %>, <%= primaryKey.type %>><% } %> { <%_ for (const relationship of relationships) { if (relationship.relationshipType === 'many-to-one' && relationship.otherEntityName === 'user' && databaseType === 'sql') { _%> @@ -107,14 +112,32 @@ public interface <%= entityClass %>Repository extends <% if (databaseType === 's } else if (databaseType === 'mongodb' || databaseType === 'couchbase') { _%> @Query("<%= (databaseType === 'mongodb') ? '{}' : '#{#n1ql.selectEntity} WHERE #{#n1ql.filter}' %>") + <% if (databaseType === 'couchbase') { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> Page<<%= asEntity(entityClass) %>> findAllWithEagerRelationships(Pageable pageable); @Query("<%= (databaseType === 'mongodb') ? '{}' : '#{#n1ql.selectEntity} WHERE #{#n1ql.filter}' %>") + <% if (databaseType === 'couchbase') { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> List<<%= asEntity(entityClass) %>> findAllWithEagerRelationships(); @Query("<%- (databaseType === 'mongodb') ? "{'id': ?0}" : "#{#n1ql.selectEntity} USE KEYS $1 WHERE #{#n1ql.filter}" %>") + <% if (databaseType === 'couchbase') { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> Optional<<%= asEntity(entityClass) %>> findOneWithEagerRelationships(<%= primaryKey.type %> id); <%_ } } _%> + <%_ if (databaseType === 'couchbase') { _%> + @Override + // Add ScanConsistency to fix issue with Spring Data Couchbase + // https://github.com/spring-projects/spring-data-couchbase/issues/897 + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + List<<%= asEntity(entityClass) %>> findAll(); + + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + List<<%= asEntity(entityClass) %>> findAll(Sort sort); + + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Page<<%= asEntity(entityClass) %>> findAll(Pageable pageable); + <%_ } _%> } diff --git a/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs b/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs index 0fbccaeb6f61..c43ed836b969 100644 --- a/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs +++ b/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs @@ -26,8 +26,11 @@ import org.springframework.data.cassandra.repository.ReactiveCassandraRepository import <%= packageName %>.repository.search.SearchCouchbaseRepository; <%_ } _%> <%_ if (databaseType === 'couchbase') { _%> -import org.springframework.data.couchbase.core.query.Query; -import org.springframework.data.couchbase.repository.ReactiveCouchbaseSortingRepository; +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.domain.Sort; +import org.springframework.data.couchbase.repository.Query; +import org.springframework.data.repository.reactive.ReactiveSortingRepository; +import org.springframework.data.couchbase.repository.ScanConsistency; <%_ } _%> <%_ if (databaseType === 'neo4j') { _%> import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository; @@ -64,7 +67,7 @@ import java.util.UUID; */ @SuppressWarnings("unused") @Repository -public interface <%= entityClass %>Repository extends <% if (databaseType === 'sql') { %>R2dbc<% } if (databaseType === 'mongodb') { %>ReactiveMongo<% } if (databaseType === 'couchbase') { %>ReactiveN1qlCouchbase<% } if (databaseType === 'neo4j') { %>ReactiveNeo4j<% } if (databaseType === 'cassandra') { %>ReactiveCassandra<% } %>Repository<<%= asEntity(entityClass) %>, <%= primaryKey.type %>><% if (searchEngine === 'couchbase') { %>, SearchCouchbaseRepository<<%= asEntity(entityClass) %>, <%= primaryKey.type %>><% } %><% if (databaseType === 'sql') { %>, <%= entityClass %>RepositoryInternal<% } %> { +public interface <%= entityClass %>Repository extends <% if (databaseType === 'sql') { %>R2dbc<% } if (databaseType === 'mongodb') { %>ReactiveMongo<% } if (databaseType === 'couchbase') { %>ReactiveSorting<% } if (databaseType === 'neo4j') { %>ReactiveNeo4j<% } if (databaseType === 'cassandra') { %>ReactiveCassandra<% } %>Repository<<%= asEntity(entityClass) %>, <%= primaryKey.type %>><% if (searchEngine === 'couchbase') { %>, SearchCouchbaseRepository<<%= asEntity(entityClass) %>, <%= primaryKey.type %>><% } %><% if (databaseType === 'sql') { %>, <%= entityClass %>RepositoryInternal<% } %> { <%_ if (pagination !== 'no') { _%> @@ -74,12 +77,15 @@ public interface <%= entityClass %>Repository extends <% if (databaseType === 's <%_ if (['couchbase', 'mongodb'].includes(databaseType)) { _%> @Query("<%= (databaseType === 'mongodb') ? '{}' : '#{#n1ql.selectEntity} WHERE #{#n1ql.filter}' %>") + <% if (databaseType === 'couchbase') { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> Flux<<%= asEntity(entityClass) %>> findAllWithEagerRelationships(Pageable pageable); @Query("<%= (databaseType === 'mongodb') ? '{}' : '#{#n1ql.selectEntity} WHERE #{#n1ql.filter}' %>") + <% if (databaseType === 'couchbase') { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> Flux<<%= asEntity(entityClass) %>> findAllWithEagerRelationships(); @Query("<%- (databaseType === 'mongodb') ? "{'id': ?0}" : "#{#n1ql.selectEntity} USE KEYS $1 WHERE #{#n1ql.filter}" %>") + <% if (databaseType === 'couchbase') { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> Mono<<%= asEntity(entityClass) %>> findOneWithEagerRelationships(<%= primaryKey.type %> id); <%_ } _%> <%_ if (databaseType === 'neo4j') { _%> @@ -93,6 +99,17 @@ public interface <%= entityClass %>Repository extends <% if (databaseType === 's Mono<<%= asEntity(entityClass) %>> findOneWithEagerRelationships(<%= primaryKey.type %> id); <%_ } _%> <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + // Add ScanConsistency to fix issue with Spring Data Couchbase + // https://github.com/spring-projects/spring-data-couchbase/issues/897 + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Flux<<%= asEntity(entityClass) %>> findAll(); + + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Flux<<%= asEntity(entityClass) %>> findAll(Sort sort); + <%_ } _%> <%_ if (databaseType === 'sql') { if (fieldsContainOwnerManyToMany) { _%> @@ -136,7 +153,7 @@ public interface <%= entityClass %>Repository extends <% if (databaseType === 's <%_ } _%> <%_ } _%> - // just to avoid having unambigous methods + // just to avoid having unambiguous methods @Override Flux<<%= asEntity(entityClass) %>> findAll(); diff --git a/generators/server/files.js b/generators/server/files.js index 3d3af18f8b45..97e937d3ccdb 100644 --- a/generators/server/files.js +++ b/generators/server/files.js @@ -895,50 +895,72 @@ const serverFiles = { ], }, { - condition: generator => !generator.reactive && generator.databaseType === 'couchbase', + condition: generator => generator.databaseType === 'couchbase', path: SERVER_MAIN_SRC_DIR, templates: [ { - file: 'package/repository/N1qlCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/N1qlCouchbaseRepository.java`, + file: 'package/config/couchbase/CustomCouchbaseRepositoryFactory.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomCouchbaseRepositoryFactory.java`, + }, + { + file: 'package/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomCouchbaseRepositoryFactoryBean.java`, + }, + { + file: 'package/config/couchbase/CustomCouchbaseRepositoryQuery.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomCouchbaseRepositoryQuery.java`, + }, + { + file: 'package/config/couchbase/CustomN1qlQueryCreator.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomN1qlQueryCreator.java`, + }, + { + file: 'package/config/couchbase/CustomN1qlRepositoryQueryExecutor.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomN1qlRepositoryQueryExecutor.java`, }, { - file: 'package/repository/CustomN1qlCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/CustomN1qlCouchbaseRepository.java`, + file: 'package/config/couchbase/package-info.java', + renameTo: generator => `${generator.javaDir}config/couchbase/package-info.java`, }, ], }, { - condition: generator => generator.searchEngine === 'couchbase', + condition: generator => !generator.reactive && generator.databaseType === 'couchbase', path: SERVER_MAIN_SRC_DIR, templates: [ { - file: 'package/repository/search/SearchCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/search/SearchCouchbaseRepository.java`, + file: 'package/repository/CustomCouchbaseRepository.java', + renameTo: generator => `${generator.javaDir}repository/CustomCouchbaseRepository.java`, }, ], }, { - condition: generator => generator.searchEngine === 'couchbase', - path: SERVER_TEST_SRC_DIR, + condition: generator => generator.reactive && generator.databaseType === 'couchbase', + path: SERVER_MAIN_SRC_DIR, templates: [ { - file: 'package/repository/CustomN1qlCouchbaseRepositoryTest.java', - renameTo: generator => `${generator.testDir}repository/CustomN1qlCouchbaseRepositoryTest.java`, + file: 'package/repository/CustomReactiveCouchbaseRepository.java', + renameTo: generator => `${generator.javaDir}repository/CustomReactiveCouchbaseRepository.java`, }, ], }, { - condition: generator => generator.reactive && generator.databaseType === 'couchbase', + condition: generator => generator.searchEngine === 'couchbase', path: SERVER_MAIN_SRC_DIR, templates: [ { - file: 'package/repository/ReactiveN1qlCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/ReactiveN1qlCouchbaseRepository.java`, + file: 'package/repository/search/SearchCouchbaseRepository.java', + renameTo: generator => `${generator.javaDir}repository/search/SearchCouchbaseRepository.java`, }, + ], + }, + { + condition: generator => generator.searchEngine === 'couchbase', + path: SERVER_TEST_SRC_DIR, + templates: [ { - file: 'package/repository/CustomReactiveN1qlCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/CustomReactiveN1qlCouchbaseRepository.java`, + file: 'package/repository/CustomCouchbaseRepositoryTest.java', + renameTo: generator => `${generator.testDir}repository/CustomCouchbaseRepositoryTest.java`, }, ], }, @@ -1185,8 +1207,8 @@ const serverFiles = { path: SERVER_TEST_SRC_DIR, templates: [ { - file: 'package/config/DatabaseConfigurationIT.java', - renameTo: generator => `${generator.testDir}config/DatabaseConfigurationIT.java`, + file: 'package/CouchbaseTestContainerExtension.java', + renameTo: generator => `${generator.testDir}CouchbaseTestContainerExtension.java`, }, ], }, diff --git a/generators/server/templates/build.gradle.ejs b/generators/server/templates/build.gradle.ejs index df48780631a7..08b73a4a6a7b 100644 --- a/generators/server/templates/build.gradle.ejs +++ b/generators/server/templates/build.gradle.ejs @@ -501,7 +501,6 @@ dependencies { <%_ if (databaseType === 'couchbase') { _%> implementation "com.github.differentway:couchmove" implementation "com.couchbase.client:java-client" - implementation "com.couchbase.client:encryption" <%_ } _%> implementation ("io.springfox:springfox-oas") implementation ("io.springfox:springfox-swagger2") diff --git a/generators/server/templates/pom.xml.ejs b/generators/server/templates/pom.xml.ejs index 1a126ad29fe6..ab2dc5dc89e8 100644 --- a/generators/server/templates/pom.xml.ejs +++ b/generators/server/templates/pom.xml.ejs @@ -365,10 +365,6 @@ com.couchbase.client java-client - - com.couchbase.client - encryption - <%_ } _%> <%_ if (databaseType === 'neo4j') { _%> diff --git a/generators/server/templates/src/main/java/package/config/DatabaseConfiguration_couchbase.java.ejs b/generators/server/templates/src/main/java/package/config/DatabaseConfiguration_couchbase.java.ejs index 4d66976b230a..6183a9b0c62f 100644 --- a/generators/server/templates/src/main/java/package/config/DatabaseConfiguration_couchbase.java.ejs +++ b/generators/server/templates/src/main/java/package/config/DatabaseConfiguration_couchbase.java.ejs @@ -20,21 +20,26 @@ package <%= packageName %>.config; import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.config.JHipsterProperties; import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.Cluster; import com.github.couchmove.Couchmove; +<%_ if (searchEngine === 'couchbase') { _%> import <%= packageName %>.repository.Custom<% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository; -<%_ if (searchEngine === 'elasticsearch') { _%> -import <%= packageName %>.repository.<% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository; +<%_ } else if (searchEngine === 'elasticsearch') { _%> +import org.springframework.data.couchbase.repository.<% if (reactive) { %>Reactive<% } %>CouchbaseRepository; <%_ } _%> + +import <%= packageName %>.config.couchbase.CustomCouchbaseRepositoryFactoryBean; + import org.apache.commons.codec.binary.Base64; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; <%_ if (searchEngine === 'elasticsearch') { _%> import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.FilterType; @@ -46,6 +51,7 @@ import org.springframework.data.elasticsearch.repository.config.Enable<% if (rea <%_ } _%> import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; +import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; import org.springframework.data.couchbase.config.BeanNames; import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; import org.springframework.data.couchbase.core.mapping.event.ValidatingCouchbaseEventListener; @@ -64,22 +70,51 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; +<%_ if (reactive) { _%> +import java.util.Set; +import java.lang.reflect.Field; +import org.springframework.data.repository.util.QueryExecutionConverters; +import reactor.core.publisher.Flux; +<%_ } _%> @Configuration <%_ if (searchEngine === 'elasticsearch') { _%> @Enable<% if (reactive) { %>Reactive<% } %>ElasticsearchRepositories("<%= packageName %>.repository.search") <%_ } _%> @Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD) -@Enable<% if (reactive) { %>Reactive<% } %>CouchbaseRepositories(repositoryBaseClass = Custom<% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository.class, basePackages = "<%= packageName %>.repository"<%_ if (searchEngine === 'elasticsearch') { %>, - includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = <% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository.class)<%_ } _%>) +@Enable<% if (reactive) { %>Reactive<% } %>CouchbaseRepositories(<% if (searchEngine === 'couchbase') { %>repositoryBaseClass = Custom<% if (reactive) { %>Reactive<% } %>CouchbaseRepository.class, <% } %>basePackages = "<%= packageName %>.repository", repositoryFactoryBeanClass = CustomCouchbaseRepositoryFactoryBean.class<%_ if (searchEngine === 'elasticsearch') { %>, + includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = <% if (reactive) { %>Reactive<% } %>CouchbaseRepository.class)<%_ } _%>) <%_ if (!reactive) { _%> -@EnableCouchbaseAuditing(auditorAwareRef = "springSecurityAuditorAware") +@EnableCouchbaseAuditing(auditorAwareRef = "springSecurityAuditorAware", dateTimeProviderRef = "") <%_ } _%> -@Import(value = CouchbaseAutoConfiguration.class) -public class DatabaseConfiguration { +public class DatabaseConfiguration extends AbstractCouchbaseConfiguration { private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); + private final JHipsterProperties jHipsterProperties; + private final CouchbaseProperties couchbaseProperties; + + public DatabaseConfiguration(JHipsterProperties jHipsterProperties, CouchbaseProperties couchbaseProperties) { + this.jHipsterProperties = jHipsterProperties; + this.couchbaseProperties = couchbaseProperties; + <% if (reactive) { %>allowPageable();<% } %> + } + +<%_ if (reactive) { _%> + // Temporary Hack to fix pageable + @SuppressWarnings("unchecked") + private void allowPageable() { + try { + Field allowed_pageable_types = QueryExecutionConverters.class.getDeclaredField("ALLOWED_PAGEABLE_TYPES"); + allowed_pageable_types.setAccessible(true); + Set> ALLOWED_PAGEABLE_TYPES = (Set>) allowed_pageable_types.get(QueryExecutionConverters.class); + ALLOWED_PAGEABLE_TYPES.add(Flux.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + +<%_ } _%> @Bean public ValidatingCouchbaseEventListener validatingCouchbaseEventListener() { return new ValidatingCouchbaseEventListener(validator()); @@ -90,6 +125,31 @@ public class DatabaseConfiguration { return new LocalValidatorFactoryBean(); } + @Override + public String getConnectionString() { + return couchbaseProperties.getConnectionString(); + } + + @Override + public String getUserName() { + return couchbaseProperties.getUsername(); + } + + @Override + public String getPassword() { + return couchbaseProperties.getPassword(); + } + + @Override + public String getBucketName() { + return jHipsterProperties.getDatabase().getCouchbase().getBucketName(); + } + + @Bean + public Bucket bucket(Cluster cluster) { + return cluster.bucket(jHipsterProperties.getDatabase().getCouchbase().getBucketName()); + } + @Bean(name = BeanNames.COUCHBASE_CUSTOM_CONVERSIONS) public CouchbaseCustomConversions customConversions() { List> converters = new ArrayList<>(); @@ -106,9 +166,9 @@ public class DatabaseConfiguration { } @Bean - public Couchmove couchmove(Bucket couchbaseBucket, CouchbaseProperties properties) { + public Couchmove couchmove(Bucket bucket, Cluster cluster) { log.debug("Configuring Couchmove"); - Couchmove couchmove = new Couchmove(couchbaseBucket, properties.getBucket().getName(), properties.getBucket().getPassword(), "config/couchmove/changelog"); + Couchmove couchmove = new Couchmove(bucket, cluster, "config/couchmove/changelog"); couchmove.migrate(); return couchmove; } @@ -201,9 +261,9 @@ public class DatabaseConfiguration { } } - /** - * Simple singleton to convert {@link UUID}s to their {@link String} representation. - */ + /** + * Simple singleton to convert {@link UUID}s to their {@link String} representation. + */ @WritingConverter public enum UUIDToStringConverter implements Converter { INSTANCE; @@ -215,8 +275,8 @@ public class DatabaseConfiguration { } /** - * Simple singleton to convert from {@link String} {@link UUID} representation. - */ + * Simple singleton to convert from {@link String} {@link UUID} representation. + */ @ReadingConverter public enum StringToUUIDConverter implements Converter { INSTANCE; diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactory.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactory.java.ejs new file mode 100644 index 000000000000..431f9a30b322 --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactory.java.ejs @@ -0,0 +1,83 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + 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 <%= packageName %>.config.couchbase; + +import java.lang.reflect.Method; +import java.util.Optional; +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; +import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; +import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.repository.core.NamedQueries; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.RepositoryQuery; + +public class CustomCouchbaseRepositoryFactory extends CouchbaseRepositoryFactory { + + private MappingContext mappingContext; + private RepositoryOperationsMapping couchbaseOperationsMapping; + + /** + * Create a new factory. + * + * @param couchbaseOperationsMapping the template for the underlying actions. + */ + public CustomCouchbaseRepositoryFactory(RepositoryOperationsMapping couchbaseOperationsMapping) { + super(couchbaseOperationsMapping); + this.couchbaseOperationsMapping = couchbaseOperationsMapping; + this.mappingContext = this.couchbaseOperationsMapping.getMappingContext(); + } + + @Override + protected Optional getQueryLookupStrategy( + QueryLookupStrategy.Key key, + QueryMethodEvaluationContextProvider contextProvider + ) { + return Optional.of(new CustomCouchbaseQueryLookupStrategy(contextProvider)); + } + + private class CustomCouchbaseQueryLookupStrategy implements QueryLookupStrategy { + + private final QueryMethodEvaluationContextProvider evaluationContextProvider; + + public CustomCouchbaseQueryLookupStrategy(QueryMethodEvaluationContextProvider evaluationContextProvider) { + this.evaluationContextProvider = evaluationContextProvider; + } + + @Override + public RepositoryQuery resolveQuery( + final Method method, + final RepositoryMetadata metadata, + final ProjectionFactory factory, + final NamedQueries namedQueries + ) { + final CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve( + metadata.getRepositoryInterface(), + metadata.getDomainType() + ); + + CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, factory, mappingContext); + return new CustomCouchbaseRepositoryQuery(couchbaseOperations, queryMethod, namedQueries); + } + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java.ejs new file mode 100644 index 000000000000..e146f5eea8bf --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java.ejs @@ -0,0 +1,40 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + 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 <%= packageName %>.config.couchbase; + +import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; +import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory; +import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactoryBean; + +public class CustomCouchbaseRepositoryFactoryBean extends CouchbaseRepositoryFactoryBean { + + /** + * Creates a new {@link CouchbaseRepositoryFactoryBean} for the given repository interface. + * + * @param repositoryInterface must not be {@literal null}. + */ + public CustomCouchbaseRepositoryFactoryBean(Class repositoryInterface) { + super(repositoryInterface); + } + + @Override + protected CouchbaseRepositoryFactory getFactoryInstance(RepositoryOperationsMapping operationsMapping) { + return new CustomCouchbaseRepositoryFactory(operationsMapping); + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryQuery.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryQuery.java.ejs new file mode 100644 index 000000000000..99f5ae1d77c8 --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryQuery.java.ejs @@ -0,0 +1,44 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + 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 <%= packageName %>.config.couchbase; + +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; +import org.springframework.data.couchbase.repository.query.CouchbaseRepositoryQuery; +import org.springframework.data.repository.core.NamedQueries; +import <%= packageName %>.config.couchbase.CustomN1qlRepositoryQueryExecutor; + +public class CustomCouchbaseRepositoryQuery extends CouchbaseRepositoryQuery { + + private CouchbaseOperations couchbaseOperations; + private CouchbaseQueryMethod queryMethod; + private NamedQueries namedQueries; + + public CustomCouchbaseRepositoryQuery(CouchbaseOperations operations, CouchbaseQueryMethod queryMethod, NamedQueries namedQueries) { + super(operations, queryMethod, namedQueries); + this.couchbaseOperations = operations; + this.queryMethod = queryMethod; + this.namedQueries = namedQueries; + } + + @Override + public Object execute(final Object[] parameters) { + return new CustomN1qlRepositoryQueryExecutor(couchbaseOperations, queryMethod, namedQueries).execute(parameters); + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlQueryCreator.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlQueryCreator.java.ejs new file mode 100644 index 000000000000..8b4583bbf9d7 --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlQueryCreator.java.ejs @@ -0,0 +1,162 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + 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 <%= packageName %>.config.couchbase; + +import static org.springframework.data.couchbase.core.query.N1QLExpression.*; + +import java.util.Iterator; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.convert.CouchbaseConverter; +import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; +import org.springframework.data.couchbase.core.query.Query; +import org.springframework.data.couchbase.core.query.QueryCriteria; +import org.springframework.data.domain.Sort; +import org.springframework.data.mapping.PersistentPropertyPath; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.query.ParameterAccessor; +import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.parser.AbstractQueryCreator; +import org.springframework.data.repository.query.parser.Part; +import org.springframework.data.repository.query.parser.PartTree; + +public class CustomN1qlQueryCreator extends AbstractQueryCreator { + + private final CouchbaseOperations couchbaseOperations; + private final ParameterAccessor accessor; + private final MappingContext context; + private final QueryMethod queryMethod; + private final CouchbaseConverter converter; + + public CustomN1qlQueryCreator( + final CouchbaseOperations operations, + final PartTree tree, + final ParameterAccessor accessor, + QueryMethod queryMethod, + CouchbaseConverter converter + ) { + super(tree, accessor); + this.accessor = accessor; + this.context = converter.getMappingContext(); + this.queryMethod = queryMethod; + this.converter = converter; + this.couchbaseOperations = operations; + } + + static Converter cvtr = source -> + new StringBuilder(source.getFieldName().length() + 2).append('`').append(source.getFieldName()).append('`').toString(); + + @Override + protected QueryCriteria create(final Part part, final Iterator iterator) { + PersistentPropertyPath path = context.getPersistentPropertyPath(part.getProperty()); + CouchbasePersistentProperty property = path.getLeafProperty(); + return from(part, property, QueryCriteria.where(addMetaIfIdProperty(path, property)), iterator); + } + + @Override + protected QueryCriteria and(Part part, QueryCriteria base, Iterator iterator) { + if (base == null) { + return create(part, iterator); + } + PersistentPropertyPath path = context.getPersistentPropertyPath(part.getProperty()); + CouchbasePersistentProperty property = path.getLeafProperty(); + return from(part, property, base.and(addMetaIfIdProperty(path, property)), iterator); + } + + private String addMetaIfIdProperty( + final PersistentPropertyPath path, + final CouchbasePersistentProperty property + ) { + return path.toDotPath(cvtr); + } + + @Override + protected QueryCriteria or(QueryCriteria base, QueryCriteria criteria) { + return base.or(criteria); + } + + @Override + protected Query complete(QueryCriteria criteria, Sort sort) { + return (criteria == null ? new Query() : new Query().addCriteria(criteria)).with(sort); + } + + private QueryCriteria from( + final Part part, + final CouchbasePersistentProperty property, + final QueryCriteria criteria, + final Iterator parameters + ) { + final Part.Type type = part.getType(); + + switch (type) { + case GREATER_THAN: + case AFTER: + return criteria.gt(parameters.next()); + case GREATER_THAN_EQUAL: + return criteria.gte(parameters.next()); + case LESS_THAN: + case BEFORE: + return criteria.lt(parameters.next()); + case LESS_THAN_EQUAL: + return criteria.lte(parameters.next()); + case SIMPLE_PROPERTY: + return criteria.eq(parameters.next()); + case NEGATING_SIMPLE_PROPERTY: + return criteria.ne(parameters.next()); + case CONTAINING: + return criteria.containing(parameters.next()); + case NOT_CONTAINING: + return criteria.notContaining(parameters.next()); + case STARTING_WITH: + return criteria.startingWith(parameters.next()); + case ENDING_WITH: + return criteria.endingWith(parameters.next()); + case LIKE: + return criteria.like(parameters.next()); + case NOT_LIKE: + return criteria.notLike(parameters.next()); + case WITHIN: + return criteria.within(parameters.next()); + case IS_NULL: + return criteria.isNull(); + case IS_NOT_NULL: + return criteria.isNotNull(); + case IS_EMPTY: + return criteria.isNotValued(); + case IS_NOT_EMPTY: + return criteria.isValued(); + case EXISTS: + return criteria.isNotMissing(); + case REGEX: + return criteria.regex(parameters.next()); + case BETWEEN: + return criteria.between(parameters.next(), parameters.next()); + case IN: + return criteria.in(parameters.next()); + case NOT_IN: + return criteria.notIn(parameters.next()); + case TRUE: + return criteria.TRUE(); + case FALSE: + return criteria.FALSE(); + default: + throw new IllegalArgumentException("Unsupported keyword!"); + } + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlRepositoryQueryExecutor.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlRepositoryQueryExecutor.java.ejs new file mode 100644 index 000000000000..519006f0ba61 --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlRepositoryQueryExecutor.java.ejs @@ -0,0 +1,102 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + 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 <%= packageName %>.config.couchbase; + +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation; +import org.springframework.data.couchbase.core.query.Query; +import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; +import org.springframework.data.couchbase.repository.query.ReactiveN1qlRepositoryQueryExecutor; +import org.springframework.data.couchbase.repository.query.StringN1qlQueryCreator; +import org.springframework.data.repository.core.NamedQueries; +import org.springframework.data.repository.query.ParameterAccessor; +import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.parser.PartTree; + +public class CustomN1qlRepositoryQueryExecutor { + + private final CouchbaseOperations operations; + private final CouchbaseQueryMethod queryMethod; + private final NamedQueries namedQueries; + + public CustomN1qlRepositoryQueryExecutor( + final CouchbaseOperations operations, + final CouchbaseQueryMethod queryMethod, + final NamedQueries namedQueries + ) { + this.operations = operations; + this.queryMethod = queryMethod; + this.namedQueries = namedQueries; + } + + /** + * see also {@link ReactiveN1qlRepositoryQueryExecutor#execute(Object[] parameters) execute } + * + * @param parameters substitute values + * @return with data + */ + public Object execute(final Object[] parameters) { + final Class domainClass = queryMethod.getResultProcessor().getReturnedType().getDomainType(); + final ParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters); + + // this is identical to ReactiveN1qlRespositoryQueryExecutor, + // except for the type of 'q', and the call to oneValue() vs one() + Query query; + ExecutableFindByQueryOperation.ExecutableFindByQuery q; + if (queryMethod.hasN1qlAnnotation()) { + query = + new StringN1qlQueryCreator( + accessor, + queryMethod, + operations.getConverter(), + operations.getBucketName(), + QueryMethodEvaluationContextProvider.DEFAULT, + namedQueries + ) + .createQuery(); + } else { + final PartTree tree = new PartTree(queryMethod.getName(), domainClass); + query = new CustomN1qlQueryCreator(operations, tree, accessor, queryMethod, operations.getConverter()).createQuery(); + } + q = + (ExecutableFindByQueryOperation.ExecutableFindByQuery) operations + .findByQuery(domainClass) + .consistentWith(buildQueryScanConsistency()) + .matching(query); + if (queryMethod.isCountQuery()) { + return q.count(); + } else if (queryMethod.isCollectionQuery()) { + return q.all(); + } else { + return q.oneValue(); + } + } + + private QueryScanConsistency buildQueryScanConsistency() { + QueryScanConsistency scanConsistency = QueryScanConsistency.NOT_BOUNDED; + if (queryMethod.hasConsistencyAnnotation()) { + scanConsistency = queryMethod.getConsistencyAnnotation().value(); + } else if (queryMethod.hasScanConsistencyAnnotation()) { + scanConsistency = queryMethod.getScanConsistencyAnnotation().query(); + } + return scanConsistency; + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/package-info.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/package-info.java.ejs new file mode 100644 index 000000000000..7c072ccbc4bb --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/package-info.java.ejs @@ -0,0 +1,25 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + 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. +-%> +/** + * Classes in this package are meant to extend Spring Data Couchbase + * and fix some of the issues encountered during integration. + */ +package <%= packageName %>.config.couchbase; + + diff --git a/generators/server/templates/src/main/java/package/domain/AbstractAuditingEntity.java.ejs b/generators/server/templates/src/main/java/package/domain/AbstractAuditingEntity.java.ejs index 650ff5219d9a..1d56f504b06b 100644 --- a/generators/server/templates/src/main/java/package/domain/AbstractAuditingEntity.java.ejs +++ b/generators/server/templates/src/main/java/package/domain/AbstractAuditingEntity.java.ejs @@ -19,7 +19,7 @@ package <%= packageName %>.domain; <%_ if (databaseType === 'couchbase') { _%> -import com.couchbase.client.java.repository.annotation.Field; +import org.springframework.data.couchbase.core.mapping.Field; <%_ } _%> import com.fasterxml.jackson.annotation.JsonIgnore; <%_ if (!reactive) { _%> diff --git a/generators/server/templates/src/main/java/package/domain/User.java.ejs b/generators/server/templates/src/main/java/package/domain/User.java.ejs index 7108f4bdee3e..9a11b6f715ed 100644 --- a/generators/server/templates/src/main/java/package/domain/User.java.ejs +++ b/generators/server/templates/src/main/java/package/domain/User.java.ejs @@ -21,7 +21,7 @@ package <%= packageName %>.domain; import <%= packageName %>.config.Constants; <%_ if (databaseType === 'couchbase') { _%> -import com.couchbase.client.java.repository.annotation.Field; +import org.springframework.data.couchbase.core.mapping.Field; <%_ } _%> <%_ if (!reactive && databaseType === 'cassandra') { _%> import com.datastax.oss.driver.api.mapper.annotations.CqlName; diff --git a/generators/server/templates/src/main/java/package/repository/AuthorityRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/AuthorityRepository.java.ejs index 0cdfee82ea9a..cf411946f5ed 100644 --- a/generators/server/templates/src/main/java/package/repository/AuthorityRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/AuthorityRepository.java.ejs @@ -26,11 +26,11 @@ import org.springframework.data.jpa.repository.JpaRepository; <%_ } else { _%> import org.springframework.data.r2dbc.repository.R2dbcRepository; <%_ } _%> -<%_ } _%> -<%_ if (databaseType === 'mongodb') { _%> +<%_ } else if (databaseType === 'mongodb') { _%> import org.springframework.data.mongodb.repository.<% if (reactive) { %>Reactive<% } %>MongoRepository; -<%_ } _%> -<%_ if (databaseType === 'neo4j') { _%> +<%_ } else if (databaseType === 'couchbase') { _%> +import org.springframework.data.couchbase.repository.<% if (reactive) { %>Reactive<% } %>CouchbaseRepository; +<%_ } else if (databaseType === 'neo4j') { _%> import org.springframework.data.neo4j.repository.<% if (reactive) { %>Reactive<% } %>Neo4jRepository; <% if (reactive) { %>import reactor.core.publisher.Flux;<% } else { %>import java.util.List;<% } %> <%_ } _%> @@ -55,7 +55,7 @@ import org.springframework.data.neo4j.repository.<% if (reactive) { %>Reactive<% * Spring Data Couchbase repository for the {@link Authority} entity. */ <%_ } _%> -public interface AuthorityRepository extends <% if (databaseType === 'sql') { %><% if (reactive) { %>R2dbc<% } else { %>Jpa<% } %>Repository<% } else { %><% if (reactive) { %>Reactive<% } %><% if (databaseType === 'mongodb') { %>MongoRepository<% } %><% if (databaseType === 'neo4j') { %>Neo4jRepository<% } %><% if (databaseType === 'couchbase') { %>N1qlCouchbaseRepository<% } %><% } %> { +public interface AuthorityRepository extends <% if (databaseType === 'sql') { %><% if (reactive) { %>R2dbc<% } else { %>Jpa<% } %>Repository<% } else { %><% if (reactive) { %>Reactive<% } %><% if (databaseType === 'mongodb') { %>MongoRepository<% } %><% if (databaseType === 'neo4j') { %>Neo4jRepository<% } %><% if (databaseType === 'couchbase') { %>CouchbaseRepository<% } %><% } %> { <%_ if (databaseType === 'neo4j') { _%> <% if (!reactive) { %>// See https://github.com/neo4j/sdn-rx/issues/51<%_ } _%> <% if (reactive) { %>Flux<% } else { %>List<% } %><<%= asEntity('Authority') %>> findAll(); diff --git a/generators/server/templates/src/main/java/package/repository/CustomN1qlCouchbaseRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/CustomCouchbaseRepository.java.ejs similarity index 86% rename from generators/server/templates/src/main/java/package/repository/CustomN1qlCouchbaseRepository.java.ejs rename to generators/server/templates/src/main/java/package/repository/CustomCouchbaseRepository.java.ejs index 7df0eed31864..c32b1a11ebba 100644 --- a/generators/server/templates/src/main/java/package/repository/CustomN1qlCouchbaseRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/CustomCouchbaseRepository.java.ejs @@ -30,7 +30,7 @@ import <%= packageName %>.repository.search.SearchCouchbaseRepository; import org.springframework.data.couchbase.core.CouchbaseOperations; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; -import org.springframework.data.couchbase.repository.support.N1qlCouchbaseRepository; +import org.springframework.data.couchbase.repository.support.SimpleCouchbaseRepository; <%_ if (searchEngine === 'couchbase') { _%> import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -48,9 +48,10 @@ import java.util.stream.Collectors; /** * A custom implementation of {@code CouchbaseRepository}. */ -public class CustomN1qlCouchbaseRepository extends N1qlCouchbaseRepository<%if (searchEngine === 'couchbase') { %> implements SearchCouchbaseRepository<% } %> { +public class CustomCouchbaseRepository extends SimpleCouchbaseRepository<%if (searchEngine === 'couchbase') { %> implements SearchCouchbaseRepository<% } %> { private final CouchbasePersistentEntity persistentEntity; + private final CouchbaseOperations couchbaseOperations; /** * Create a new Repository. @@ -58,9 +59,10 @@ public class CustomN1qlCouchbaseRepository extends N * @param metadata the Metadata for the entity. * @param couchbaseOperations the reference to the template used. */ - public CustomN1qlCouchbaseRepository(CouchbaseEntityInformation metadata, CouchbaseOperations couchbaseOperations) { + public CustomCouchbaseRepository(CouchbaseEntityInformation metadata, CouchbaseOperations couchbaseOperations) { super(metadata, couchbaseOperations); - persistentEntity = getCouchbaseOperations().getConverter().getMappingContext().getPersistentEntity(getEntityInformation().getJavaType()); + this.couchbaseOperations = couchbaseOperations; + persistentEntity = couchbaseOperations.getConverter().getMappingContext().getPersistentEntity(getEntityInformation().getJavaType()); } @Override @@ -125,7 +127,7 @@ public class CustomN1qlCouchbaseRepository extends N if (getEntityInformation().getId(entity) != null) { return entity; } - setId(entity, getCouchbaseOperations().getGeneratedId(entity)); + //setId(entity, getCouchbaseOperations().getGeneratedId(entity)); return entity; } diff --git a/generators/server/templates/src/main/java/package/repository/CustomReactiveN1qlCouchbaseRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/CustomReactiveCouchbaseRepository.java.ejs similarity index 71% rename from generators/server/templates/src/main/java/package/repository/CustomReactiveN1qlCouchbaseRepository.java.ejs rename to generators/server/templates/src/main/java/package/repository/CustomReactiveCouchbaseRepository.java.ejs index 6c540f6cbeae..cb385e944487 100644 --- a/generators/server/templates/src/main/java/package/repository/CustomReactiveN1qlCouchbaseRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/CustomReactiveCouchbaseRepository.java.ejs @@ -28,16 +28,14 @@ import com.couchbase.client.java.search.result.SearchQueryRow; import <%= packageName %>.repository.search.SearchCouchbaseRepository; <%_ } _%> import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.couchbase.core.CouchbaseTemplate; -import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; +import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; +import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; -import org.springframework.data.couchbase.repository.support.ReactiveN1qlCouchbaseRepository; +import org.springframework.data.couchbase.repository.support.SimpleReactiveCouchbaseRepository; <%_ if (searchEngine === 'couchbase') { _%> import org.springframework.data.domain.Pageable; <%_ } _%> -import org.springframework.data.repository.util.QueryExecutionConverters; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; <%_ if (searchEngine === 'couchbase') { _%> import rx.Observable; @@ -45,7 +43,6 @@ import rx.RxReactiveStreams; <%_ } _%> import java.io.Serializable; -import java.lang.reflect.Field; import java.util.Set; <%_ if (searchEngine === 'couchbase') { _%> import java.util.LinkedList; @@ -55,37 +52,24 @@ import java.util.List; /** * A custom implementation of {@code CouchbaseRepository}. */ -public class CustomReactiveN1qlCouchbaseRepository extends ReactiveN1qlCouchbaseRepository<%if (searchEngine === 'couchbase') { %> implements SearchCouchbaseRepository<% } %> { +public class CustomReactiveCouchbaseRepository extends SimpleReactiveCouchbaseRepository<%if (searchEngine === 'couchbase') { %> implements SearchCouchbaseRepository<% } %> { private final CouchbasePersistentEntity persistentEntity; @Autowired - private CouchbaseTemplate template; + private final ReactiveCouchbaseTemplate template; /** * Create a new Repository. * * @param metadata the Metadata for the entity. - * @param couchbaseOperations the reference to the template used. + * @param couchbaseOperations the reference to the operations used. + * @param couchbaseTemplate the reference to the template used. */ - public CustomReactiveN1qlCouchbaseRepository(CouchbaseEntityInformation metadata, RxJavaCouchbaseOperations couchbaseOperations) { + public CustomReactiveCouchbaseRepository(CouchbaseEntityInformation metadata, ReactiveCouchbaseOperations couchbaseOperations, ReactiveCouchbaseTemplate couchbaseTemplate) { super(metadata, couchbaseOperations); - persistentEntity = getCouchbaseOperations().getConverter().getMappingContext().getPersistentEntity(getEntityInformation().getJavaType()); - template = new CouchbaseTemplate(getCouchbaseOperations().getCouchbaseClusterInfo(), getCouchbaseOperations().getCouchbaseBucket()); - allowPageable(); - } - - // Temporary Hack to fix pageable - @SuppressWarnings("unchecked") - private void allowPageable() { - try { - Field allowed_pageable_types = QueryExecutionConverters.class.getDeclaredField("ALLOWED_PAGEABLE_TYPES"); - allowed_pageable_types.setAccessible(true); - Set> ALLOWED_PAGEABLE_TYPES = (Set>) allowed_pageable_types.get(QueryExecutionConverters.class); - ALLOWED_PAGEABLE_TYPES.add(Flux.class); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } + persistentEntity = couchbaseOperations.getConverter().getMappingContext().getPersistentEntity(getEntityInformation().getJavaType()); + template = couchbaseTemplate; } @Override @@ -147,7 +131,7 @@ public class CustomReactiveN1qlCouchbaseRepository e if (getEntityInformation().getId(entity) != null) { return entity; } - setId(entity, template.getGeneratedId(entity)); + // setId(entity, template.getGeneratedId(entity)); return entity; } diff --git a/generators/server/templates/src/main/java/package/repository/N1qlCouchbaseRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/N1qlCouchbaseRepository.java.ejs deleted file mode 100644 index 19773b451fcb..000000000000 --- a/generators/server/templates/src/main/java/package/repository/N1qlCouchbaseRepository.java.ejs +++ /dev/null @@ -1,46 +0,0 @@ -<%# - Copyright 2013-2021 the original author or authors from the JHipster project. - - This file is part of the JHipster project, see https://www.jhipster.tech/ - for more information. - - 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 <%= packageName %>.repository; - -import org.springframework.data.couchbase.core.query.Query; -import org.springframework.data.couchbase.repository.CouchbasePagingAndSortingRepository; -import org.springframework.data.repository.NoRepositoryBean; - -import java.io.Serializable; -import java.util.List; - -/** - * Couchbase specific {@link org.springframework.data.repository.Repository} interface uses N1QL for all requests. - */ -@NoRepositoryBean -public interface N1qlCouchbaseRepository extends CouchbasePagingAndSortingRepository { - - @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}") - List findAll(); - - @Query("SELECT count(*) FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}") - long count(); - - @Query("DELETE FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} returning #{#n1ql.fields}") - T removeAll(); - - default void deleteAll() { - removeAll(); - } -} diff --git a/generators/server/templates/src/main/java/package/repository/PersistentTokenRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/PersistentTokenRepository.java.ejs index a4386e00b6f5..1257369ce1ba 100644 --- a/generators/server/templates/src/main/java/package/repository/PersistentTokenRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/PersistentTokenRepository.java.ejs @@ -41,14 +41,13 @@ import java.time.LocalDate; <%_ } _%> <%_ if (databaseType === 'sql') { _%> import org.springframework.data.jpa.repository.JpaRepository; -<%_ } _%> -<%_ if (databaseType === 'mongodb') { _%> +<%_ } else if (databaseType === 'mongodb') { _%> import org.springframework.data.mongodb.repository.MongoRepository; -<%_ } _%> -<%_ if (databaseType === 'neo4j') { _%> -import org.springframework.data.neo4j.repository.Neo4jRepository; -<%_ } _%> -<%_ if (databaseType === 'cassandra') { _%> +<%_ } else if (databaseType === 'neo4j') { _%> +import org.neo4j.springframework.data.repository.Neo4jRepository; +<%_ } else if (databaseType === 'couchbase') { _%> +import org.springframework.data.couchbase.repository.CouchbaseRepository; +<%_ } else if (databaseType === 'cassandra') { _%> import org.springframework.boot.autoconfigure.cassandra.CassandraProperties; import org.springframework.stereotype.Repository; @@ -82,7 +81,7 @@ import static <%= packageName %>.config.Constants.ID_DELIMITER; <%_ } _%> */ <%_ if (['sql', 'mongodb', 'neo4j', 'couchbase'].includes(databaseType)) { _%> -public interface PersistentTokenRepository extends <% if (databaseType === 'sql') { %>JpaRepository<% } %><% if (databaseType === 'mongodb') { %>MongoRepository<% } %><% if (databaseType === 'neo4j') { %>Neo4jRepository<% } %><% if (databaseType === 'couchbase') { %>N1qlCouchbaseRepository<% } %> { +public interface PersistentTokenRepository extends <% if (databaseType === 'sql') { %>JpaRepository<% } %><% if (databaseType === 'mongodb') { %>MongoRepository<% } %><% if (databaseType === 'neo4j') { %>Neo4jRepository<% } %><% if (databaseType === 'couchbase') { %>CouchbaseRepository<% } %> { <%_ if (databaseType === 'couchbase') { _%> default Optional findBySeries(String series) { diff --git a/generators/server/templates/src/main/java/package/repository/ReactiveN1qlCouchbaseRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/ReactiveN1qlCouchbaseRepository.java.ejs deleted file mode 100644 index 54abafa34bfd..000000000000 --- a/generators/server/templates/src/main/java/package/repository/ReactiveN1qlCouchbaseRepository.java.ejs +++ /dev/null @@ -1,47 +0,0 @@ -<%# - Copyright 2013-2021 the original author or authors from the JHipster project. - - This file is part of the JHipster project, see https://www.jhipster.tech/ - for more information. - - 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 <%= packageName %>.repository; - -import org.springframework.data.couchbase.core.query.Query; -import org.springframework.data.couchbase.repository.ReactiveCouchbaseSortingRepository; -import org.springframework.data.repository.NoRepositoryBean; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.io.Serializable; - -/** - * Couchbase specific {@link org.springframework.data.repository.Repository} interface uses N1QL for all requests. - */ -@NoRepositoryBean -public interface ReactiveN1qlCouchbaseRepository extends ReactiveCouchbaseSortingRepository { - - @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}") - Flux findAll(); - - @Query("SELECT count(*) FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}") - Mono count(); - - @Query("DELETE FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} returning #{#n1ql.fields}") - Flux removeAll(); - - default Mono deleteAll() { - return removeAll().then(); - } -} diff --git a/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs index 4a8869bcb498..fb8d53c3fef8 100644 --- a/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs @@ -67,6 +67,9 @@ import org.springframework.data.domain.Page; <%_ } _%> import org.springframework.data.domain.Pageable; <%_ } _%> +<% if (databaseType === 'couchbase') { _%> +import org.springframework.data.domain.Sort; +<%_ } _%> <%_ if (databaseType === 'sql') { _%> <%_ if (!reactive) { _%> import org.springframework.data.jpa.repository.EntityGraph; @@ -88,9 +91,13 @@ import static org.springframework.data.relational.core.query.Query.query; <%_ } _%> <%_ if (databaseType === 'mongodb') { _%> import org.springframework.data.mongodb.repository.<% if (reactive) { %>Reactive<% } %>MongoRepository; -<%_ } _%> -<%_ if (databaseType === 'neo4j') { _%> +<%_ } else if (databaseType === 'neo4j') { _%> import org.springframework.data.neo4j.repository.<% if (reactive) { %>Reactive<% } %>Neo4jRepository; +<%_ } else if (databaseType === 'couchbase') { _%> +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.couchbase.repository.Query; +import org.springframework.data.couchbase.repository.ScanConsistency; +import org.springframework.data.couchbase.repository.<% if (reactive) { %>Reactive<% } %>CouchbaseRepository; <%_ } _%> <%_ if (reactive && databaseType === 'cassandra') { _%> import org.springframework.data.cassandra.ReactiveResultSet; @@ -160,24 +167,38 @@ import static <%= packageName %>.config.Constants.ID_DELIMITER; _%> <%_ if ((databaseType === 'sql' && !reactive) || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { _%> @Repository -public interface UserRepository extends <% if (databaseType === 'sql') { %>JpaRepository<<%= asEntity('User') %>, <%= user.primaryKey.type %>><% } %><% if (reactive) { %>Reactive<% } %><% if (databaseType === 'mongodb') { %>MongoRepository<<%= asEntity('User') %>, String><% } %><% if (databaseType === 'neo4j') { %>Neo4jRepository<<%= asEntity('User') %>, String><% } %><% if (databaseType === 'couchbase') { %>N1qlCouchbaseRepository<<%= asEntity('User') %>, String><%if (searchEngine === 'couchbase') { %>, SearchCouchbaseRepository<<%= asEntity('User') %>, String><% } } %> { +public interface UserRepository extends <% if (databaseType === 'sql') { %>JpaRepository<<%= asEntity('User') %>, <%= user.primaryKey.type %>><% } %><% if (reactive) { %>Reactive<% } %><% if (databaseType === 'mongodb') { %>MongoRepository<<%= asEntity('User') %>, String><% } %><% if (databaseType === 'neo4j') { %>Neo4jRepository<<%= asEntity('User') %>, String><% } %><% if (databaseType === 'couchbase') { %>CouchbaseRepository<<%= asEntity('User') %>, String><%if (searchEngine === 'couchbase') { %>, SearchCouchbaseRepository<<%= asEntity('User') %>, String><% } } %> { <%_ if (cacheManagerIsAvailable === true) { _%> String USERS_BY_LOGIN_CACHE = "usersByLogin"; String USERS_BY_EMAIL_CACHE = "usersByEmail"; + + <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + // @ScanConsistency is to fix index issues with Spring Data Couchbase + // https://github.com/spring-projects/spring-data-couchbase/issues/897 <%_ } _%> <%_ if (authenticationType !== 'oauth2') { _%> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> <%= optionalOrMono %><<%= asEntity('User') %>> findOneByActivationKey(String activationKey); <%_ } _%> <%_ if (authenticationType !== 'oauth2') { _%> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> <%= listOrFlux %><<%= asEntity('User') %>> findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(Instant dateTime); <%_ } _%> <%_ if (authenticationType !== 'oauth2') { _%> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> <%= optionalOrMono %><<%= asEntity('User') %>> findOneByResetKey(String resetKey); <%_ } _%> @@ -187,6 +208,10 @@ public interface UserRepository extends <% if (databaseType === 'sql') { %>JpaRe @Cacheable(cacheNames = USERS_BY_EMAIL_CACHE) <%_ } _%> <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + @Query("#{#n1ql.selectEntity} WHERE LOWER(email) = LOWER($1) AND #{#n1ql.filter}") + <%_ } _%> <%= optionalOrMono %><<%= asEntity('User') %>> findOneByEmailIgnoreCase(String email); <%_ } _%> @@ -194,6 +219,9 @@ public interface UserRepository extends <% if (databaseType === 'sql') { %>JpaRe <%_ if (cacheManagerIsAvailable === true) { _%> @Cacheable(cacheNames = USERS_BY_LOGIN_CACHE) <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> default <%= optionalOrMono %><<%= asEntity('User') %>> findOneByLogin(String login) { return findById(<%= asEntity('User') %>.PREFIX + ID_DELIMITER + login); } @@ -210,12 +238,27 @@ public interface UserRepository extends <% if (databaseType === 'sql') { %>JpaRe <% if (!reactive) { %>// See https://github.com/neo4j/sdn-rx/issues/51<% } %> <%= listOrFlux %><<%= asEntity('User') %>> findAll(); + <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%= listOrFlux %><<%= asEntity('User') %>> findAll(); + + <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%= listOrFlux %><<%= asEntity('User') %>> findAll(Sort sort); + <%_ } _%> <%_ if (databaseType === 'sql') { _%> @EntityGraph(attributePaths = "authorities") <%_ if (cacheManagerIsAvailable === true) { _%> @Cacheable(cacheNames = USERS_BY_LOGIN_CACHE) <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> Optional<<%= asEntity('User') %>> findOneWithAuthoritiesByLogin(String login); <%_ if (authenticationType !== 'oauth2') { _%> @@ -223,20 +266,44 @@ public interface UserRepository extends <% if (databaseType === 'sql') { %>JpaRe <%_ if (cacheManagerIsAvailable === true) { _%> @Cacheable(cacheNames = USERS_BY_EMAIL_CACHE) <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> Optional<<%= asEntity('User') %>> findOneWithAuthoritiesByEmailIgnoreCase(String email); <%_ } _%> <%_ } _%> <% if (reactive) { %> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> Flux<<%= asEntity('User') %>>findAllByIdNotNull(Pageable pageable); + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND meta(#{#n1ql.bucket}).id is not null AND activated = true") + <%_ } _%> Flux<<%= asEntity('User') %>>findAllByIdNotNullAndActivatedIsTrue(Pageable pageable); Mono count(); <% } else { %> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND meta(#{#n1ql.bucket}).id is not null AND activated = true") + <%_ } _%> Page<<%= asEntity('User') %>> findAllByIdNotNullAndActivatedIsTrue(Pageable pageable); <% } %> + + <%_ if (databaseType === 'couchbase') { _%> + @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND meta(#{#n1ql.bucket}).id is not null AND activated = true") + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%= listOrFlux %><<%= asEntity('User') %>> findAllByIdNotNullAndActivatedIsTrue(); + + @Query("SELECT COUNT(*) as count FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} AND meta(#{#n1ql.bucket}).id is not null AND activated = true") + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Long countAllByIdNotNullAndActivatedIsTrue(); + <%_ } _%> } <%_ } else if (databaseType === 'sql' && reactive) { _%> @Repository diff --git a/generators/server/templates/src/main/java/package/service/UserService.java.ejs b/generators/server/templates/src/main/java/package/service/UserService.java.ejs index b9360cafeba4..f87b88d53b36 100644 --- a/generators/server/templates/src/main/java/package/service/UserService.java.ejs +++ b/generators/server/templates/src/main/java/package/service/UserService.java.ejs @@ -56,6 +56,9 @@ import org.springframework.cache.CacheManager; <%_ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'neo4j' || databaseType === 'couchbase') { _%> <%_ if (!reactive) { _%> import org.springframework.data.domain.Page; +<%_ if (databaseType === 'couchbase') { _%> +import org.springframework.data.domain.PageImpl; +<%_ } _%> <%_ } _%> import org.springframework.data.domain.Pageable; <%_ if (authenticationType !== 'oauth2') { _%> @@ -749,7 +752,21 @@ public class UserService { @Transactional(readOnly = true) <%_ } _%> public <% if (reactive) { %>Flux<% } else { %>Page<% } %><<%= asDto('User') %>> getAllPublicUsers(Pageable pageable) { + <%_ if (databaseType === 'couchbase' && !reactive) { _%> + // FIXME: 2/1/21 Current version of Spring Data doesnt seem to support Pagination parameters. Changed the + // method to return a list and added a count query to construct the page. + // return userRepository.findAllByIdNotNullAndActivatedIsTrue(pageable).map(<%= asDto('User') %>::new); + + final Long count = userRepository.countAllByIdNotNullAndActivatedIsTrue(); + if (count == 0) { + return <% if (reactive) { %>Flux<% } else { %>Page<% } %>.empty(); + } + + final List users = userRepository.findAllByIdNotNullAndActivatedIsTrue(); + return new PageImpl<>(users.stream().map(UserDTO::new).collect(Collectors.toList()), pageable, count); + <%_ } else { _%> return userRepository.findAllByIdNotNullAndActivatedIsTrue(pageable).map(<%= asDto('User') %>::new); + <%_ } _%> } <%_ if (reactive) { _%> diff --git a/generators/server/templates/src/main/resources/config/application-dev.yml.ejs b/generators/server/templates/src/main/resources/config/application-dev.yml.ejs index 723a072a12a0..a4d6e796bdf8 100644 --- a/generators/server/templates/src/main/resources/config/application-dev.yml.ejs +++ b/generators/server/templates/src/main/resources/config/application-dev.yml.ejs @@ -196,10 +196,9 @@ spring: <%_ } _%> <%_ if (databaseType === 'couchbase') { _%> couchbase: - bootstrap-hosts: localhost - bucket: - name: <%= baseName %> - password: password + connection-string: couchbase://localhost + username: Administrator + password: password <%_ } _%> <%_ if (databaseType === 'sql') { _%> liquibase: @@ -295,6 +294,11 @@ jhipster: app1: /api,/v3/api-docs # recommended dev configuration <%_ } _%> <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + database: + couchbase: + bucket-name: <%= baseName %> + <%_ } _%> <%_ if (cacheProvider !== 'no') { _%> cache: # Cache configuration <%_ if (cacheProvider === 'hazelcast') { _%> diff --git a/generators/server/templates/src/main/resources/config/application-prod.yml.ejs b/generators/server/templates/src/main/resources/config/application-prod.yml.ejs index 8767539f11da..b7d6e5a34ca3 100644 --- a/generators/server/templates/src/main/resources/config/application-prod.yml.ejs +++ b/generators/server/templates/src/main/resources/config/application-prod.yml.ejs @@ -170,10 +170,9 @@ spring: <%_ } _%> <%_ if (databaseType === 'couchbase') { _%> couchbase: - bootstrap-hosts: localhost - bucket: - name: <%= baseName %> - password: password + connection-string: couchbase://localhost + username: Administrator + password: password <%_ } _%> <%_ if (databaseType === 'sql') { _%> # Replace by 'prod, faker' to add the faker context and have sample data loaded in production @@ -278,6 +277,11 @@ jhipster: http: cache: # Used by the CachingHttpHeadersFilter timeToLiveInDays: 1461 + <%_ if (databaseType === 'couchbase') { _%> + database: + couchbase: + bucket-name: <%= baseName %> + <%_ } _%> <%_ if (cacheProvider !== 'no') { _%> cache: # Cache configuration <%_ if (cacheProvider === 'hazelcast') { _%> diff --git a/generators/server/templates/src/test/java/package/CouchbaseTestContainerExtension.java.ejs b/generators/server/templates/src/test/java/package/CouchbaseTestContainerExtension.java.ejs new file mode 100644 index 000000000000..05bb183296d8 --- /dev/null +++ b/generators/server/templates/src/test/java/package/CouchbaseTestContainerExtension.java.ejs @@ -0,0 +1,55 @@ +<%# + Copyright 2013-2020 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + 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 + + http://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 <%= packageName %>; + +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.testcontainers.couchbase.BucketDefinition; +import org.testcontainers.couchbase.CouchbaseContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class CouchbaseTestContainerExtension implements BeforeAllCallback { + + private static AtomicBoolean started = new AtomicBoolean(false); + + private static CouchbaseContainer couchbase; + + private String getBucketName() { + return "testBucket"; + } + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + if (!started.get()) { + DockerImageName dockerImage = DockerImageName.parse("<%= DOCKER_COUCHBASE %>") + .asCompatibleSubstituteFor("couchbase/server"); + couchbase = new CouchbaseContainer(dockerImage) + .withBucket(new BucketDefinition(getBucketName()).withQuota(100)) + .withCredentials("user", "secret"); + couchbase.start(); + System.setProperty("spring.couchbase.connection-string", couchbase.getConnectionString()); + System.setProperty("spring.couchbase.username", couchbase.getUsername()); + System.setProperty("spring.couchbase.password", couchbase.getPassword()); + System.setProperty("jhipster.database.couchbase.bucket-name", getBucketName()); + started.set(true); + } + } +} diff --git a/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs b/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs index 886271575252..041ec450fe32 100644 --- a/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs +++ b/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs @@ -19,7 +19,9 @@ package <%= packageName %>; import <%= packageName %>.<%= mainClass %>; -<%_ if (databaseType === 'neo4j') { _%> +<%_ if (databaseType === 'couchbase') { _%> +import <%= packageName %>.CouchbaseTestContainerExtension; +<%_ } else if (databaseType === 'neo4j') { _%> import <%= packageName %>.AbstractNeo4jIT; <%_ } _%> <%_ if (reactiveSqlTestContainers) { _%> @@ -31,7 +33,7 @@ import <%= packageName %>.RedisTestContainerExtension; <%_ if (authenticationType === 'oauth2') { _%> import <%= packageName %>.config.TestSecurityConfiguration; <%_ } _%> -<%_ if (cacheProvider === 'redis' || databaseType === 'neo4j' || reactiveSqlTestContainers) { _%> +<%_ if (cacheProvider === 'redis' || ['neo4j', 'couchbase'].includes(databaseType) || reactiveSqlTestContainers) { _%> import org.junit.jupiter.api.extension.ExtendWith; <%_ } _%> @@ -55,7 +57,9 @@ import java.lang.annotation.Target; <%_ if (cacheProvider === 'redis') { _%> @ExtendWith(RedisTestContainerExtension.class) <%_ } _%> -<%_ if (databaseType === 'neo4j') { _%> +<%_ if (databaseType === 'couchbase') { _%> +@ExtendWith(CouchbaseTestContainerExtension.class) +<%_ } else if (databaseType === 'neo4j') { _%> @ExtendWith(AbstractNeo4jIT.class) <%_ } _%> <%_ if (reactiveSqlTestContainers) { _%> diff --git a/generators/server/templates/src/test/java/package/config/DatabaseConfigurationIT.java.ejs b/generators/server/templates/src/test/java/package/config/DatabaseConfigurationIT.java.ejs deleted file mode 100644 index cf7378b25d2f..000000000000 --- a/generators/server/templates/src/test/java/package/config/DatabaseConfigurationIT.java.ejs +++ /dev/null @@ -1,112 +0,0 @@ -<%# - Copyright 2013-2021 the original author or authors from the JHipster project. - - This file is part of the JHipster project, see https://www.jhipster.tech/ - for more information. - - 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 <%= packageName %>.config; - -import com.couchbase.client.java.Cluster; -import com.couchbase.client.java.CouchbaseCluster; -import com.couchbase.client.java.env.CouchbaseEnvironment; -import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; -import org.assertj.core.util.Lists; -import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; -import org.springframework.data.couchbase.config.BeanNames; -import org.springframework.data.couchbase.config.CouchbaseConfigurer; -import org.testcontainers.couchbase.BucketDefinition; -import org.testcontainers.couchbase.CouchbaseContainer; - -import java.util.List; - -@Configuration -public class DatabaseConfigurationIT extends AbstractCouchbaseConfiguration { - - private CouchbaseProperties couchbaseProperties; - - private static CouchbaseContainer couchbaseContainer; - - private static CouchbaseEnvironment couchbaseEnvironment; - - private static CouchbaseCluster couchbaseCluster; - - public DatabaseConfigurationIT(CouchbaseProperties couchbaseProperties) { - this.couchbaseProperties = couchbaseProperties; - } - - @Override - @Bean(destroyMethod = "", name = BeanNames.COUCHBASE_ENV) - public CouchbaseEnvironment couchbaseEnvironment() { - return getCouchbaseEnvironment(); - } - - @Override - public Cluster couchbaseCluster() throws Exception { - return getCouchbaseCluster(); - } - - @Override - protected List getBootstrapHosts() { - return Lists.newArrayList(getCouchbaseContainer().getContainerIpAddress()); - } - - @Override - protected String getBucketName() { - return couchbaseProperties.getBucket().getName(); - } - - @Override - protected String getBucketPassword() { - return couchbaseProperties.getBucket().getPassword(); - } - - @Override - protected CouchbaseConfigurer couchbaseConfigurer() { - return this; - } - - private CouchbaseContainer getCouchbaseContainer() { - if (couchbaseContainer == null) { - couchbaseContainer = new CouchbaseContainer("<%= DOCKER_COUCHBASE %>") - .withBucket(new BucketDefinition(getBucketName()).withQuota(100)) - .withCredentials(getUsername(), getBucketPassword()); - couchbaseContainer.start(); - } - return couchbaseContainer; - } - - private CouchbaseEnvironment getCouchbaseEnvironment() { - if (couchbaseEnvironment == null) { - couchbaseEnvironment = DefaultCouchbaseEnvironment - .builder() - .bootstrapCarrierDirectPort(getCouchbaseContainer().getBootstrapCarrierDirectPort()) - .bootstrapHttpDirectPort(getCouchbaseContainer().getBootstrapHttpDirectPort()) - .build(); - } - return couchbaseEnvironment; - } - - private CouchbaseCluster getCouchbaseCluster() { - if (couchbaseCluster == null) { - couchbaseCluster = CouchbaseCluster.create(couchbaseEnvironment(), - getCouchbaseContainer().getContainerIpAddress() - ); - } - return couchbaseCluster; - } -} diff --git a/generators/server/templates/src/test/java/package/repository/CustomN1qlCouchbaseRepositoryTest.java.ejs b/generators/server/templates/src/test/java/package/repository/CustomCouchbaseRepositoryTest.java.ejs similarity index 90% rename from generators/server/templates/src/test/java/package/repository/CustomN1qlCouchbaseRepositoryTest.java.ejs rename to generators/server/templates/src/test/java/package/repository/CustomCouchbaseRepositoryTest.java.ejs index a252c05a0eaa..e4a182179c66 100644 --- a/generators/server/templates/src/test/java/package/repository/CustomN1qlCouchbaseRepositoryTest.java.ejs +++ b/generators/server/templates/src/test/java/package/repository/CustomCouchbaseRepositoryTest.java.ejs @@ -28,12 +28,12 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; -class CustomN1qlCouchbaseRepositoryTest { +class CustomCouchbaseRepositoryTest { @MethodSource @ParameterizedTest void queryString(String query, AbstractFtsQuery ftsQuery) { - assertThat(Custom<% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository.queryString(query).toString()).isEqualTo(ftsQuery.toString()); + assertThat(Custom<% if (reactive) { %>Reactive<% } %>CouchbaseRepository.queryString(query).toString()).isEqualTo(ftsQuery.toString()); } @SuppressWarnings("unused") diff --git a/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs b/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs index 78a126fd15f6..448a1b829c07 100644 --- a/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs +++ b/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs @@ -20,6 +20,10 @@ package <%= packageName %>.web.rest; <%_ if (databaseType === 'cassandra') { _%> import <%= packageName %>.AbstractCassandraTest; +<%_ } else if (databaseType === 'couchbase') { _%> +import <%= packageName %>.CouchbaseTestContainerExtension; +<%_ } else if (databaseType === 'neo4j') { _%> +import <%= packageName %>.AbstractNeo4jIT; <%_ } _%> import <%= packageName %>.IntegrationTest; import <%= packageName %>.config.Constants; @@ -45,10 +49,13 @@ import <%= packageName %>.service.dto.<%= asDto('User') %>; import <%= packageName %>.web.rest.vm.KeyAndPasswordVM; import <%= packageName %>.web.rest.vm.ManagedUserVM; import org.apache.commons.lang3.RandomStringUtils; -<%_ if (reactive && testsNeedCsrf || databaseType === 'mongodb' || databaseType === 'neo4j') { _%> +<%_ if (reactive && testsNeedCsrf || ['mongodb', 'neo4j'].includes(databaseType)) { _%> import org.junit.jupiter.api.BeforeEach; <%_ } _%> import org.junit.jupiter.api.Test; +<%_ if (['neo4j', 'couchbase'].includes(databaseType)) { _%> +import org.junit.jupiter.api.extension.ExtendWith; +<%_ } _%> import org.springframework.beans.factory.annotation.Autowired; <%_ if (!reactive) { _%> import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; diff --git a/generators/server/templates/src/test/java/package/web/rest/TestUtil.java.ejs b/generators/server/templates/src/test/java/package/web/rest/TestUtil.java.ejs index 1a3ca78d3b4e..bf10c3e2decf 100644 --- a/generators/server/templates/src/test/java/package/web/rest/TestUtil.java.ejs +++ b/generators/server/templates/src/test/java/package/web/rest/TestUtil.java.ejs @@ -124,7 +124,7 @@ public final class TestUtil { <%_ if (databaseType === 'couchbase') { _%> /** - * Mock user authentication for Spring SpEL expression used in {@link <%= packageName %>.repository.N1qlCouchbaseRepository}. + * Mock user authentication */ public static void mockAuthentication() { TestSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", null)); diff --git a/generators/server/templates/src/test/resources/config/application.yml.ejs b/generators/server/templates/src/test/resources/config/application.yml.ejs index bcf5e002b3f7..0c8fc5de82f4 100644 --- a/generators/server/templates/src/test/resources/config/application.yml.ejs +++ b/generators/server/templates/src/test/resources/config/application.yml.ejs @@ -135,12 +135,6 @@ spring: keyspace-name: cassandra_unit_keyspace local-datacenter: datacenter1 <%_ } _%> - <%_ if (databaseType === 'couchbase') { _%> - couchbase: - bucket: - name: jhipster - password: password - <%_ } _%> <%_ if (databaseType === 'sql') { _%> liquibase: contexts: test diff --git a/test/app.spec.js b/test/app.spec.js index 3473f6527069..9ee6d9e4b4c6 100644 --- a/test/app.spec.js +++ b/test/app.spec.js @@ -600,7 +600,7 @@ describe('JHipster generator', () => { .on('end', done); }); - it('creates expected files with "Couchbbase FTS"', () => { + it('creates expected files with "Couchbase FTS"', () => { assert.file(expectedFiles.couchbase); assert.file(expectedFiles.couchbaseSearch); }); diff --git a/test/utils/expected-files.js b/test/utils/expected-files.js index 05a95d17278c..f39c14ce924b 100644 --- a/test/utils/expected-files.js +++ b/test/utils/expected-files.js @@ -755,9 +755,12 @@ const expectedFiles = { ], couchbase: [ - `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/repository/N1qlCouchbaseRepository.java`, - `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/repository/CustomN1qlCouchbaseRepository.java`, - `${SERVER_TEST_SRC_DIR}com/mycompany/myapp/config/DatabaseConfigurationIT.java`, + `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/config/couchbase/CustomCouchbaseRepositoryFactory.java`, + `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java`, + `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/config/couchbase/CustomCouchbaseRepositoryQuery.java`, + `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/config/couchbase/CustomN1qlQueryCreator.java`, + `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/config/couchbase/CustomN1qlRepositoryQueryExecutor.java`, + `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/config/couchbase/package-info.java`, `${SERVER_MAIN_RES_DIR}config/couchmove/changelog/V0__create_indexes.n1ql`, `${SERVER_MAIN_RES_DIR}config/couchmove/changelog/V0.1__initial_setup/ROLE_ADMIN.json`, `${SERVER_MAIN_RES_DIR}config/couchmove/changelog/V0.1__initial_setup/ROLE_USER.json`, @@ -777,8 +780,9 @@ const expectedFiles = { ], couchbaseSearch: [ + `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/repository/CustomCouchbaseRepository.java`, `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/repository/search/SearchCouchbaseRepository.java`, - `${SERVER_TEST_SRC_DIR}com/mycompany/myapp/repository/CustomN1qlCouchbaseRepositoryTest.java`, + `${SERVER_TEST_SRC_DIR}com/mycompany/myapp/repository/CustomCouchbaseRepositoryTest.java`, ], cassandra: [