From 8fd0b0bf6d498d46b57e83a956498d39cc889e2b Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 28 Apr 2014 14:21:45 -0700 Subject: [PATCH] Fixes JIRA issue SGF-277 - OQL Join in Repository Interface. --- .../query/PartTreeGemfireRepositoryQuery.java | 6 +- .../gemfire/repository/query/QueryString.java | 39 +++--- .../StringBasedGemfireRepositoryQuery.java | 34 +++-- .../support/GemfireRepositoryFactory.java | 6 +- .../gemfire/repository/sample/Account.java | 99 +++++++++++++++ .../repository/sample/AccountRepository.java | 33 +++++ .../sample/AnimalRepositoryTest.java | 2 +- .../repository/sample/CatRepository.java | 2 +- .../gemfire/repository/sample/Customer.java | 116 ++++++++++++++++++ .../repository/sample/CustomerRepository.java | 39 ++++++ .../repository/sample/DogRepository.java | 2 +- .../repository/sample/PersonRepository.java | 4 +- ...sitoryQueriesWithJoinsIntegrationTest.java | 92 ++++++++++++++ .../sample/repositoryQueriesWithJoins.xml | 35 ++++++ 14 files changed, 470 insertions(+), 39 deletions(-) create mode 100644 src/test/java/org/springframework/data/gemfire/repository/sample/Account.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/sample/AccountRepository.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/sample/Customer.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/sample/CustomerRepository.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/sample/RepositoryQueriesWithJoinsIntegrationTest.java create mode 100644 src/test/resources/org/springframework/data/gemfire/repository/sample/repositoryQueriesWithJoins.xml diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java b/src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java index 4e3edc06..13f77d86 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java @@ -60,10 +60,10 @@ public class PartTreeGemfireRepositoryQuery extends GemfireRepositoryQuery { */ @Override public Object execute(Object[] parameters) { - ParametersParameterAccessor parameterAccessor = new ParametersParameterAccessor(method.getParameters(), parameters); - QueryString query = new GemfireQueryCreator(tree, method.getPersistentEntity()).createQuery(parameterAccessor - .getSort()); + + QueryString query = new GemfireQueryCreator(tree, method.getPersistentEntity()) + .createQuery(parameterAccessor.getSort()); RepositoryQuery repositoryQuery = new StringBasedGemfireRepositoryQuery(query.toString(), method, template); diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java b/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java index 16649820..80fc5ac4 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java @@ -21,23 +21,23 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.gemstone.gemfire.cache.Region; + import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import com.gemstone.gemfire.cache.Region; - /** * Value object to work with OQL query strings. * * @author Oliver Gierke * @author David Turanski + * @author John Blum */ class QueryString { - //private static final String REGION_PATTERN = "(?<=\\/)\\w+"; - private static final String REGION_PATTERN = "\\/(\\/?\\w)+"; - private static final String IN_PARAMETER_PATTERN = "(?<=IN (SET|LIST) \\$)\\d"; private static final String IN_PATTERN = "(?<=IN (SET|LIST) )\\$\\d"; + private static final String IN_PARAMETER_PATTERN = "(?<=IN (SET|LIST) \\$)\\d"; + private static final String REGION_PATTERN = "\\/(\\/?\\w)+"; private final String query; @@ -47,8 +47,7 @@ class QueryString { * @param source */ public QueryString(String source) { - - Assert.hasText(source); + Assert.hasText(source, "The OQL statement (Query) to execute must be specified!"); this.query = source; } @@ -57,7 +56,8 @@ class QueryString { * * @param domainClass must not be {@literal null}. */ - public QueryString(Class domainClass) { + @SuppressWarnings("unused") + public QueryString(Class domainClass) { this(domainClass, false); } @@ -68,8 +68,7 @@ class QueryString { * @param isCountQuery indicates if this is a count query */ public QueryString(Class domainClass, boolean isCountQuery) { - this(String - .format(isCountQuery ? "SELECT count(*) FROM /%s" : "SELECT * FROM /%s", domainClass.getSimpleName())); + this(String.format(isCountQuery ? "SELECT count(*) FROM /%s" : "SELECT * FROM /%s", domainClass.getSimpleName())); } /** @@ -79,6 +78,7 @@ class QueryString { * @param region must not be {@literal null}. * @return */ + @SuppressWarnings("unused") public QueryString forRegion(Class domainClass, Region region) { return new QueryString(query.replaceAll(REGION_PATTERN, region.getFullPath())); } @@ -90,23 +90,21 @@ class QueryString { * @param values the values to bind, returns the {@link QueryString} as is if {@literal null} is given. * @return */ - public QueryString bindIn(Collection values) { + public QueryString bindIn(Collection values) { + if (values != null) { + String valueString = StringUtils.collectionToDelimitedString(values, ", ", "'", "'"); + return new QueryString(query.replaceFirst(IN_PATTERN, String.format("(%s)", valueString))); + } - if (values == null) { - return this; - } + return this; + } - String valueString = StringUtils.collectionToDelimitedString(values, ", ", "'", "'"); - return new QueryString(query.replaceFirst(IN_PATTERN, String.format("(%s)", valueString))); - } - - /** + /** * Returns the parameter indexes used in this query. * * @return the parameter indexes used in this query or an empty {@link Iterable} if none are used. */ public Iterable getInParameterIndexes() { - Pattern pattern = Pattern.compile(IN_PARAMETER_PATTERN); Matcher matcher = pattern.matcher(query); List result = new ArrayList(); @@ -126,4 +124,5 @@ class QueryString { public String toString() { return query; } + } diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java b/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java index b956a424..26afa5e7 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java @@ -38,11 +38,13 @@ import com.gemstone.gemfire.cache.query.internal.ResultsBag; */ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery { - private static final String INVALID_EXECUTION = "Paging and modifying queries are not supported!"; + private static final String INVALID_QUERY = "Paging and modifying queries are not supported!"; + + private boolean userDefinedQuery = false; - private final QueryString query; private final GemfireQueryMethod method; private final GemfireTemplate template; + private final QueryString query; /* * (non-Javadoc) @@ -69,34 +71,50 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery { * Creates a new {@link StringBasedGemfireRepositoryQuery} using the given query {@link String}, * {@link GemfireQueryMethod} and {@link GemfireTemplate}. * - * @param query will fall back to the query annotated to the given {@link GemfireQueryMethod} if {@literal null} is - * given. + * @param query will fall back to the query annotated to the given {@link GemfireQueryMethod} if {@literal null}. * @param method must not be {@literal null}. * @param template must not be {@literal null}. */ public StringBasedGemfireRepositoryQuery(String query, GemfireQueryMethod method, GemfireTemplate template) { - super(method); Assert.notNull(template); + this.userDefinedQuery |= !StringUtils.hasText(query); this.query = new QueryString(StringUtils.hasText(query) ? query : method.getAnnotatedQuery()); this.method = method; this.template = template; if (method.isPageQuery() || method.isModifyingQuery()) { - throw new IllegalStateException(INVALID_EXECUTION); + throw new IllegalStateException(INVALID_QUERY); } } - /* + /* + * (non-Javadoc) + */ + public StringBasedGemfireRepositoryQuery asUserDefinedQuery() { + this.userDefinedQuery = true; + return this; + } + + /* + * (non-Javadoc) + */ + public boolean isUserDefinedQuery() { + return userDefinedQuery; + } + + /* * (non-Javadoc) * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[]) */ @Override public Object execute(Object[] parameters) { ParametersParameterAccessor accessor = new ParametersParameterAccessor(method.getParameters(), parameters); - QueryString query = this.query.forRegion(method.getEntityInformation().getJavaType(), template.getRegion()); + + QueryString query = (isUserDefinedQuery() ? this.query : + this.query.forRegion(method.getEntityInformation().getJavaType(), template.getRegion())); for (Integer index : query.getInParameterIndexes()) { query = query.bindIn(toCollection(accessor.getBindableValue(index - 1))); diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java index 44327a3e..31d676c8 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java +++ b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java @@ -147,18 +147,18 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport { return new QueryLookupStrategy() { @Override public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) { - GemfireQueryMethod queryMethod = new GemfireQueryMethod(method, metadata, context); GemfireTemplate template = getTemplate(metadata); if (queryMethod.hasAnnotatedQuery()) { - return new StringBasedGemfireRepositoryQuery(queryMethod, template); + return new StringBasedGemfireRepositoryQuery(queryMethod, template).asUserDefinedQuery(); } String namedQueryName = queryMethod.getNamedQueryName(); + if (namedQueries.hasQuery(namedQueryName)) { return new StringBasedGemfireRepositoryQuery(namedQueries.getQuery(namedQueryName), queryMethod, - template); + template).asUserDefinedQuery(); } return new PartTreeGemfireRepositoryQuery(queryMethod, template); diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/Account.java b/src/test/java/org/springframework/data/gemfire/repository/sample/Account.java new file mode 100644 index 00000000..beafdb46 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/Account.java @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2013 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.gemfire.repository.sample; + +import org.springframework.data.annotation.Id; +import org.springframework.data.gemfire.mapping.Region; +import org.springframework.util.Assert; + +/** + * The Account class is an abstract data type (ADT) for modeling customer accounts. + * + * @author John Blum + * @see org.springframework.data.gemfire.mapping.Region + * @since 1.0.0 + */ +@Region("Accounts") +@SuppressWarnings("unused") +public class Account { + + @Id + private Long id; + + private Long customerId; + + private String number; + + public Account(final Long customerId) { + Assert.notNull(customerId, "The Customer ID to which this Account is associated cannot be null!"); + this.customerId = customerId; + } + + public Account(final Customer customer) { + this(customer.getId()); + } + + public Account(final Long customerId, final String number) { + this(customerId); + this.number = number; + } + + public Account(final Customer customer, final String number) { + this(customer); + this.number = number; + } + + public Account(final Long accountId, final Long customerId) { + this(customerId); + this.id = accountId; + } + + public Account(final Long accountId, final Customer customer) { + this(customer); + this.id = accountId; + } + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(final Long customerId) { + this.customerId = customerId; + } + + public String getNumber() { + return number; + } + + public void setNumber(final String number) { + this.number = number; + } + + @Override + public String toString() { + return String.format("Customer (%1$d) Account (%2$d) #(%3$s)", getCustomerId(), getId(), getNumber()); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/AccountRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/AccountRepository.java new file mode 100644 index 00000000..363940c1 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/AccountRepository.java @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2013 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.gemfire.repository.sample; + +import org.springframework.data.gemfire.repository.GemfireRepository; + +/** + * The AccountsRepository class is a Data Access Object (DAO) for accessing and performing persistent operations on + * Account objects. + * + * @author John Blum + * @see org.springframework.data.gemfire.repository.sample.Account + * @see org.springframework.data.gemfire.repository.GemfireRepository + * @since 1.0.0 + */ +@SuppressWarnings("unused") +public interface AccountRepository extends GemfireRepository { + +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/AnimalRepositoryTest.java b/src/test/java/org/springframework/data/gemfire/repository/sample/AnimalRepositoryTest.java index 2e169a98..5fdd9bee 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/AnimalRepositoryTest.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/AnimalRepositoryTest.java @@ -38,7 +38,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @since 1.4.0 * @link https://github.com/spring-projects/spring-data-gemfire/pull/55 */ -@ContextConfiguration("AnimalRepositoryTest-context.xml") +@ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) @SuppressWarnings("unused") public class AnimalRepositoryTest { diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/CatRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/CatRepository.java index d2eac8ec..2238a2d8 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/CatRepository.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/CatRepository.java @@ -29,7 +29,7 @@ public interface CatRepository extends GemfireRepository { Animal findByName(String name); - @Query("SELECT * FROM /Animals x WHERE x.name = $1") + @Query("SELECT * FROM /Cats x WHERE x.name = $1") Animal findBy(String name); } diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/Customer.java b/src/test/java/org/springframework/data/gemfire/repository/sample/Customer.java new file mode 100644 index 00000000..6dab9cc1 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/Customer.java @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2013 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.gemfire.repository.sample; + +import org.springframework.data.annotation.Id; +import org.springframework.data.gemfire.mapping.Region; +import org.springframework.util.ObjectUtils; + +/** + * The Customer class is a class abstraction modeling a Customer. + * + * @author John Blum + * @see org.springframework.data.annotation.Id + * @see org.springframework.data.gemfire.mapping.Region + * @since 1.0.0 + */ +@SuppressWarnings("unused") +@Region("Customers") +public class Customer { + + @Id + private Long id; + + private String firstName; + private String lastName; + + public Customer() { + } + + public Customer(final Long id) { + this.id = id; + } + + public Customer(final String firstName, final String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(final String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(final String lastName) { + this.lastName = lastName; + } + + protected static boolean equalsIgnoreNull(final Object obj1, final Object obj2) { + return (obj1 == null ? obj2 == null : obj1.equals(obj2)); + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + + if (!(obj instanceof Customer)) { + return false; + } + + final Customer that = (Customer) obj; + + return equalsIgnoreNull(this.getId(), that.getId()) + && ObjectUtils.nullSafeEquals(this.getFirstName(), that.getFirstName()) + && ObjectUtils.nullSafeEquals(this.getLastName(), that.getLastName()); + } + + protected static int hashCodeIgnoreNull(final Object obj) { + return (obj != null ? obj.hashCode() : 0); + } + + @Override + public int hashCode() { + int hashValue = 17; + hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getId()); + hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getFirstName()); + hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getLastName()); + return hashValue; + } + + @Override + public String toString() { + return String.format("%1$s %2$s", getFirstName(), getLastName()); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/CustomerRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/CustomerRepository.java new file mode 100644 index 00000000..e1daf9f1 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/CustomerRepository.java @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2013 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.gemfire.repository.sample; + +import java.util.List; + +import org.springframework.data.gemfire.repository.GemfireRepository; +import org.springframework.data.gemfire.repository.Query; + +/** + * The CustomerRepository class is a Data Access Object (DAO) for accessing and performing persistent operations on + * Customer objects. + * + * @author John Blum + * @see org.springframework.data.gemfire.repository.sample.Customer + * @see org.springframework.data.gemfire.repository.GemfireRepository + * @since 1.0.0 + */ +@SuppressWarnings("unused") +public interface CustomerRepository extends GemfireRepository { + + @Query("SELECT DISTINCT c FROM /Customers c, /Accounts a WHERE c.id = a.customerId") + List findCustomersWithAccounts(); + +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/DogRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/DogRepository.java index 0eee1271..25721c19 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/DogRepository.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/DogRepository.java @@ -29,7 +29,7 @@ public interface DogRepository extends GemfireRepository { Animal findByName(String name); - @Query("SELECT * FROM /Animals x WHERE x.name = $1") + @Query("SELECT * FROM /Dogs x WHERE x.name = $1") Animal findBy(String name); } diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java index e00b3263..2e3b8795 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java @@ -28,10 +28,10 @@ import org.springframework.data.gemfire.repository.Query; */ public interface PersonRepository extends GemfireRepository { - @Query("SELECT * FROM /Person p WHERE p.firstname = $1") + @Query("SELECT * FROM /simple p WHERE p.firstname = $1") Collection findByFirstnameAnnotated(String firstname); - @Query("SELECT * FROM /Person p WHERE p.firstname IN SET $1") + @Query("SELECT * FROM /simple p WHERE p.firstname IN SET $1") Collection findByFirstnamesAnnotated(Collection firstnames); Collection findByFirstname(String firstname); diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/RepositoryQueriesWithJoinsIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/repository/sample/RepositoryQueriesWithJoinsIntegrationTest.java new file mode 100644 index 00000000..738e22d8 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/RepositoryQueriesWithJoinsIntegrationTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2010-2013 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.gemfire.repository.sample; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * The RepositoryQueriesWithJoinsTest class is a test suite of test cases testing the use of JOINS between 2 Regions + * in GemFire OQL queries (SELECT statements). + * + * @author John Blum + * @see org.junit.Test + * @see org.junit.runner.RunWith + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @since 1.0.0 + */ +@ContextConfiguration("repositoryQueriesWithJoins.xml") +@RunWith(SpringJUnit4ClassRunner.class) +@SuppressWarnings("unused") +public class RepositoryQueriesWithJoinsIntegrationTest { + + private static final AtomicLong ID_SEQUENCE = new AtomicLong(0l); + + @Autowired + private AccountRepository accountRepo; + + @Autowired + private CustomerRepository customerRepo; + + protected Account createAccount(final Customer customer, final String number) { + Account account = new Account(ID_SEQUENCE.incrementAndGet(), customer); + account.setNumber(number); + return account; + } + + protected Customer createCustomer(final String firstName, final String lastName) { + Customer customer = new Customer(firstName, lastName); + customer.setId(ID_SEQUENCE.incrementAndGet()); + return customer; + } + + @Test + public void testJoinQueries() { + Customer jonDoe = customerRepo.save(createCustomer("Jon", "Doe")); + Customer janeDoe = customerRepo.save(createCustomer("Jane", "Doe")); + Customer jackHandy = customerRepo.save(createCustomer("Jack", "Handy")); + + Account jonAccountOne = accountRepo.save(createAccount(jonDoe, "1")); + Account jonAccountTwo = accountRepo.save(createAccount(jonDoe, "2")); + Account janeAccount = accountRepo.save(createAccount(janeDoe, "1")); + + List actualCustomersWithAccounts = customerRepo.findCustomersWithAccounts(); + List expectedCustomersWithAccounts = Arrays.asList(jonDoe, janeDoe); + + assertNotNull(actualCustomersWithAccounts); + assertFalse(String.format("Expected Customers (%1$s)!", expectedCustomersWithAccounts), + actualCustomersWithAccounts.isEmpty()); + assertEquals(String.format("Expected Customers (%1$s); but was (%2$s)!", expectedCustomersWithAccounts, actualCustomersWithAccounts), + 2, actualCustomersWithAccounts.size()); + assertTrue(String.format("Expected Customers (%1$s); but was (%2$s)!", expectedCustomersWithAccounts, actualCustomersWithAccounts), + actualCustomersWithAccounts.containsAll(expectedCustomersWithAccounts)); + } + +} diff --git a/src/test/resources/org/springframework/data/gemfire/repository/sample/repositoryQueriesWithJoins.xml b/src/test/resources/org/springframework/data/gemfire/repository/sample/repositoryQueriesWithJoins.xml new file mode 100644 index 00000000..0f23e203 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/repository/sample/repositoryQueriesWithJoins.xml @@ -0,0 +1,35 @@ + + + + + RepositoryQueriesWithJoinsIntegrationTest + config + 0 + + + + + + + + + + + + + +