DATACOUCH-388 Add support for ANSI-JOIN across entities
Motivation ---------- To support ANSI JOIN across associated entities Changes ------- N1qlJoin annotation on an associated entity field is discovered by the couchbaseTemplate, which uses N1qlJoinResolver to build and resolve the query. The query can be resolved eagerly or lazily based on the fetch type configuration. The retrieved results are then mapped to the associated entity. In the lazy resolver, a proxy is set on the property which resolves on the first access. Results ------- Verified by unit and integration tests. ANSI join is now possible across entities. Original PR: #174.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* 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 org.springframework.data.couchbase.repository.join;
|
||||
import java.util.List;
|
||||
|
||||
import com.couchbase.client.java.repository.annotation.Field;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.couchbase.core.query.FetchType;
|
||||
import org.springframework.data.couchbase.core.query.N1qlJoin;
|
||||
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
|
||||
|
||||
/**
|
||||
* Author test class for N1QL Join tests
|
||||
*/
|
||||
@N1qlPrimaryIndexed
|
||||
public class Author {
|
||||
@Id
|
||||
String id;
|
||||
|
||||
@Field("name")
|
||||
String name;
|
||||
|
||||
@N1qlJoin(on = "lks.name=rks.authorName", fetchType = FetchType.IMMEDIATE)
|
||||
List<Book> books;
|
||||
|
||||
public Author(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setBooks(List<Book> books) {
|
||||
this.books = books;
|
||||
}
|
||||
|
||||
public List<Book> getBooks() {
|
||||
return books;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* 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 org.springframework.data.couchbase.repository.join;
|
||||
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.couchbase.client.java.cluster.ClusterInfo;
|
||||
import org.springframework.data.couchbase.config.BeanNames;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
|
||||
/**
|
||||
* Populates author and book documents for N1ql Join tests
|
||||
*
|
||||
*/
|
||||
public class AuthorAndBookPopulatorListener extends DependencyInjectionTestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void beforeTestClass(final TestContext testContext) throws Exception {
|
||||
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
|
||||
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
|
||||
populateTestData(client, clusterInfo);
|
||||
}
|
||||
|
||||
void populateTestData(Bucket client, ClusterInfo clusterInfo) {
|
||||
CouchbaseTemplate template = new CouchbaseTemplate(clusterInfo, client);
|
||||
for(int i=0;i<5;i++) {
|
||||
Author author = new Author("Author" + i,"foo"+ i);
|
||||
template.save(author);
|
||||
for (int j=0;j<5;j++) {
|
||||
Book book = new Book("Book" + i+j, "foo"+i, "");
|
||||
template.save(book);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* 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 org.springframework.data.couchbase.repository.join;
|
||||
|
||||
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
|
||||
import org.springframework.data.couchbase.repository.CouchbaseRepository;
|
||||
|
||||
@N1qlPrimaryIndexed
|
||||
public interface AuthorRepository extends CouchbaseRepository<Author, String> {
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* 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 org.springframework.data.couchbase.repository.join;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
/**
|
||||
* Book test class for N1QL Join tests
|
||||
*/
|
||||
public class Book {
|
||||
@Id
|
||||
String name;
|
||||
|
||||
String authorName;
|
||||
|
||||
String description;
|
||||
|
||||
public Book(String name, String authorName, String description) {
|
||||
this.name = name;
|
||||
this.authorName = authorName;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getAuthorName() {
|
||||
return this.authorName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* 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 org.springframework.data.couchbase.repository.join;
|
||||
|
||||
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
|
||||
import org.springframework.data.couchbase.repository.CouchbaseRepository;
|
||||
|
||||
@N1qlSecondaryIndexed(indexName = "bookIndex")
|
||||
interface BookRepository extends CouchbaseRepository<Book, String> {
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* 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 org.springframework.data.couchbase.repository.join;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.couchbase.ContainerResourceRunner;
|
||||
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
|
||||
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
|
||||
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
|
||||
import org.springframework.data.couchbase.repository.support.IndexManager;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
|
||||
/**
|
||||
* N1ql Join tests
|
||||
*
|
||||
* @author Subhashni Balakrishnan
|
||||
*/
|
||||
@RunWith(ContainerResourceRunner.class)
|
||||
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
|
||||
@TestExecutionListeners(listeners = {AuthorAndBookPopulatorListener.class})
|
||||
public class N1qlJoinTests {
|
||||
|
||||
@Autowired
|
||||
private RepositoryOperationsMapping operationsMapping;
|
||||
|
||||
@Autowired
|
||||
private IndexManager indexManager;
|
||||
|
||||
private BookRepository bookRepository;
|
||||
|
||||
private AuthorRepository authorRepository;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
RepositoryFactorySupport factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
|
||||
bookRepository = factory.getRepository(BookRepository.class);
|
||||
authorRepository = factory.getRepository(AuthorRepository.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testN1qlJoin() {
|
||||
Author a = authorRepository.findById("Author" + 1).get();
|
||||
assertTrue(a.books.size() == 5);
|
||||
for(Book b:a.books) {
|
||||
assertEquals("Join on author name mismatch", a.name, b.authorName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testN1qlJoinWithNoResults() {
|
||||
final String name = "testN1qlJoinWithNoResults";
|
||||
Author a = new Author(name, name);
|
||||
authorRepository.save(a);
|
||||
|
||||
Author saveda = authorRepository.findById(name).get();
|
||||
assertTrue(saveda.books.isEmpty());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user