Browse Source
Various cleanups to the Spring Data JPA example, including: * Move repositories into service package and make them package private thus only expose the service interfaces to clients. * Merge HotelRepository and HotelSummaryRepository and make service implementations package protected. * Introduce integration test base class to bootstrap the app as SpringAppliation.run would. * Refactor central test case to rather use Spring MVC integration testing framework. * Add integration tests for repositories to execute query methods.pull/10/head
12 changed files with 243 additions and 259 deletions
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-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.boot.sample.data.jpa.domain.repository; |
||||
|
||||
import org.springframework.boot.sample.data.jpa.domain.City; |
||||
import org.springframework.boot.sample.data.jpa.domain.Hotel; |
||||
import org.springframework.data.repository.Repository; |
||||
|
||||
public interface HotelRepository extends Repository<Hotel, Long> { |
||||
|
||||
Hotel findByCityAndName(City city, String name); |
||||
|
||||
} |
||||
@ -1,106 +0,0 @@
@@ -1,106 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-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.boot.sample.data.jpa.domain.repository; |
||||
|
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
|
||||
import javax.persistence.EntityManager; |
||||
import javax.persistence.PersistenceContext; |
||||
import javax.persistence.Query; |
||||
|
||||
import org.springframework.boot.sample.data.jpa.domain.City; |
||||
import org.springframework.boot.sample.data.jpa.domain.Hotel; |
||||
import org.springframework.boot.sample.data.jpa.domain.HotelSummary; |
||||
import org.springframework.boot.sample.data.jpa.domain.RatingCount; |
||||
import org.springframework.data.domain.Page; |
||||
import org.springframework.data.domain.PageImpl; |
||||
import org.springframework.data.domain.Pageable; |
||||
import org.springframework.data.domain.Sort; |
||||
import org.springframework.data.domain.Sort.Order; |
||||
import org.springframework.stereotype.Repository; |
||||
|
||||
@Repository |
||||
public class HotelSummaryRepository { |
||||
|
||||
private static final String AVERAGE_REVIEW_FUNCTION = "avg(r.rating)"; |
||||
|
||||
private static final String FIND_BY_CITY_QUERY = "select new " |
||||
+ HotelSummary.class.getName() + "(h.city, h.name, " |
||||
+ AVERAGE_REVIEW_FUNCTION |
||||
+ ") from Hotel h left outer join h.reviews r where h.city = ?1 group by h"; |
||||
|
||||
private static final String FIND_BY_CITY_COUNT_QUERY = "select count(h) from Hotel h where h.city = ?1"; |
||||
|
||||
private static final String FIND_RATING_COUNTS_QUERY = "select new " |
||||
+ RatingCount.class.getName() + "(r.rating, count(r)) " |
||||
+ "from Review r where r.hotel = ?1 group by r.rating order by r.rating DESC"; |
||||
|
||||
private EntityManager entityManager; |
||||
|
||||
public Page<HotelSummary> findByCity(City city, Pageable pageable) { |
||||
StringBuilder queryString = new StringBuilder(FIND_BY_CITY_QUERY); |
||||
applySorting(queryString, pageable == null ? null : pageable.getSort()); |
||||
|
||||
Query query = this.entityManager.createQuery(queryString.toString()); |
||||
query.setParameter(1, city); |
||||
query.setFirstResult(pageable.getOffset()); |
||||
query.setMaxResults(pageable.getPageSize()); |
||||
|
||||
Query countQuery = this.entityManager.createQuery(FIND_BY_CITY_COUNT_QUERY); |
||||
countQuery.setParameter(1, city); |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
List<HotelSummary> content = query.getResultList(); |
||||
|
||||
Long total = (Long) countQuery.getSingleResult(); |
||||
|
||||
return new PageImpl<HotelSummary>(content, pageable, total); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public List<RatingCount> findRatingCounts(Hotel hotel) { |
||||
Query query = this.entityManager.createQuery(FIND_RATING_COUNTS_QUERY); |
||||
query.setParameter(1, hotel); |
||||
return query.getResultList(); |
||||
} |
||||
|
||||
private void applySorting(StringBuilder query, Sort sort) { |
||||
if (sort != null) { |
||||
query.append(" order by"); |
||||
for (Order order : sort) { |
||||
String aliasedProperty = getAliasedProperty(order.getProperty()); |
||||
query.append(String.format(" %s %s,", aliasedProperty, order |
||||
.getDirection().name().toLowerCase(Locale.US))); |
||||
} |
||||
query.deleteCharAt(query.length() - 1); |
||||
} |
||||
} |
||||
|
||||
private String getAliasedProperty(String property) { |
||||
if (property.equals("averageRating")) { |
||||
return AVERAGE_REVIEW_FUNCTION; |
||||
} |
||||
return "h." + property; |
||||
} |
||||
|
||||
@PersistenceContext |
||||
public void setEntityManager(EntityManager entityManager) { |
||||
this.entityManager = entityManager; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* |
||||
* Copyright 2012-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.boot.sample.data.jpa.service; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.springframework.boot.sample.data.jpa.domain.City; |
||||
import org.springframework.boot.sample.data.jpa.domain.Hotel; |
||||
import org.springframework.boot.sample.data.jpa.domain.HotelSummary; |
||||
import org.springframework.boot.sample.data.jpa.domain.RatingCount; |
||||
import org.springframework.data.domain.Page; |
||||
import org.springframework.data.domain.Pageable; |
||||
import org.springframework.data.jpa.repository.Query; |
||||
import org.springframework.data.repository.Repository; |
||||
|
||||
interface HotelRepository extends Repository<Hotel, Long> { |
||||
|
||||
Hotel findByCityAndName(City city, String name); |
||||
|
||||
@Query("select new org.springframework.boot.sample.data.jpa.domain.HotelSummary(h.city, h.name, avg(r.rating)) " |
||||
+ "from Hotel h left outer join h.reviews r where h.city = ?1 group by h") |
||||
Page<HotelSummary> findByCity(City city, Pageable pageable); |
||||
|
||||
@Query("select new org.springframework.boot.sample.data.jpa.domain.RatingCount(r.rating, count(r)) " |
||||
+ "from Review r where r.hotel = ?1 group by r.rating order by r.rating DESC") |
||||
List<RatingCount> findRatingCounts(Hotel hotel); |
||||
} |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
/* |
||||
* Copyright 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.boot.sample.data.jpa; |
||||
|
||||
import org.junit.runner.RunWith; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.context.initializer.ConfigFileApplicationContextInitializer; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; |
||||
import org.springframework.test.context.web.WebAppConfiguration; |
||||
|
||||
/** |
||||
* Base class for integration tests. Mimics the behavior of |
||||
* {@link SpringApplication#run(String...)}. |
||||
* |
||||
* @author Oliver Gierke |
||||
*/ |
||||
@RunWith(SpringJUnit4ClassRunner.class) |
||||
@WebAppConfiguration |
||||
@ContextConfiguration(classes = SampleDataJpaApplication.class, initializers = ConfigFileApplicationContextInitializer.class) |
||||
public abstract class AbstractIntegrationTests { |
||||
|
||||
} |
||||
@ -1,87 +1,37 @@
@@ -1,87 +1,37 @@
|
||||
/* |
||||
* Copyright 2012-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.boot.sample.data.jpa; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.concurrent.Callable; |
||||
import java.util.concurrent.Executors; |
||||
import java.util.concurrent.Future; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import org.junit.AfterClass; |
||||
import org.junit.BeforeClass; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.http.client.ClientHttpResponse; |
||||
import org.springframework.web.client.DefaultResponseErrorHandler; |
||||
import org.springframework.web.client.RestTemplate; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.test.web.servlet.MockMvc; |
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* Basic integration tests for service demo application. |
||||
* Integration test to run the application. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public class SampleDataJpaApplicationTests { |
||||
public class SampleDataJpaApplicationTests extends AbstractIntegrationTests { |
||||
|
||||
private static ConfigurableApplicationContext context; |
||||
@Autowired |
||||
private WebApplicationContext context; |
||||
|
||||
@BeforeClass |
||||
public static void start() throws Exception { |
||||
Future<ConfigurableApplicationContext> future = Executors |
||||
.newSingleThreadExecutor().submit( |
||||
new Callable<ConfigurableApplicationContext>() { |
||||
@Override |
||||
public ConfigurableApplicationContext call() throws Exception { |
||||
return (ConfigurableApplicationContext) SpringApplication |
||||
.run(SampleDataJpaApplication.class); |
||||
} |
||||
}); |
||||
context = future.get(30, TimeUnit.SECONDS); |
||||
} |
||||
private MockMvc mvc; |
||||
|
||||
@AfterClass |
||||
public static void stop() { |
||||
if (context != null) { |
||||
context.close(); |
||||
} |
||||
@Before |
||||
public void setUp() { |
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); |
||||
} |
||||
|
||||
@Test |
||||
public void testHome() throws Exception { |
||||
ResponseEntity<String> entity = getRestTemplate().getForEntity( |
||||
"http://localhost:8080", String.class); |
||||
assertEquals(HttpStatus.OK, entity.getStatusCode()); |
||||
assertEquals("Bath", entity.getBody()); |
||||
} |
||||
|
||||
private RestTemplate getRestTemplate() { |
||||
RestTemplate restTemplate = new RestTemplate(); |
||||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { |
||||
@Override |
||||
public void handleError(ClientHttpResponse response) throws IOException { |
||||
} |
||||
}); |
||||
return restTemplate; |
||||
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk()) |
||||
.andExpect(content().string("Bath")); |
||||
} |
||||
|
||||
} |
||||
|
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
/* |
||||
* Copyright 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.boot.sample.data.jpa.service; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.sample.data.jpa.AbstractIntegrationTests; |
||||
import org.springframework.boot.sample.data.jpa.domain.City; |
||||
import org.springframework.data.domain.Page; |
||||
import org.springframework.data.domain.PageRequest; |
||||
|
||||
import static org.hamcrest.CoreMatchers.is; |
||||
import static org.junit.Assert.assertThat; |
||||
|
||||
/** |
||||
* Integration tests for {@link CityRepository}. |
||||
* |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public class CityRepositoryIntegrationTests extends AbstractIntegrationTests { |
||||
|
||||
@Autowired |
||||
CityRepository repository; |
||||
|
||||
@Test |
||||
public void findsFirstPageOfCities() { |
||||
|
||||
Page<City> cities = this.repository.findAll(new PageRequest(0, 10)); |
||||
assertThat(cities.getTotalElements(), is(21L)); |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Copyright 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.boot.sample.data.jpa.service; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.sample.data.jpa.AbstractIntegrationTests; |
||||
import org.springframework.boot.sample.data.jpa.domain.City; |
||||
import org.springframework.boot.sample.data.jpa.domain.Hotel; |
||||
import org.springframework.boot.sample.data.jpa.domain.HotelSummary; |
||||
import org.springframework.boot.sample.data.jpa.domain.Rating; |
||||
import org.springframework.boot.sample.data.jpa.domain.RatingCount; |
||||
import org.springframework.data.domain.Page; |
||||
import org.springframework.data.domain.PageRequest; |
||||
import org.springframework.data.domain.Sort.Direction; |
||||
|
||||
import static org.hamcrest.Matchers.hasSize; |
||||
import static org.hamcrest.Matchers.is; |
||||
import static org.junit.Assert.assertThat; |
||||
|
||||
/** |
||||
* Integration tests for {@link HotelRepository}. |
||||
* |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public class HotelRepositoryIntegrationTests extends AbstractIntegrationTests { |
||||
|
||||
@Autowired |
||||
CityRepository cityRepository; |
||||
@Autowired |
||||
HotelRepository repository; |
||||
|
||||
@Test |
||||
public void executesQueryMethodsCorrectly() { |
||||
City city = this.cityRepository |
||||
.findAll(new PageRequest(0, 1, Direction.ASC, "name")).getContent() |
||||
.get(0); |
||||
assertThat(city.getName(), is("Atlanta")); |
||||
|
||||
Page<HotelSummary> hotels = this.repository.findByCity(city, new PageRequest(0, |
||||
10, Direction.ASC, "name")); |
||||
Hotel hotel = this.repository.findByCityAndName(city, hotels.getContent().get(0) |
||||
.getName()); |
||||
assertThat(hotel.getName(), is("Doubletree")); |
||||
|
||||
List<RatingCount> counts = this.repository.findRatingCounts(hotel); |
||||
assertThat(counts, hasSize(1)); |
||||
assertThat(counts.get(0).getRating(), is(Rating.AVERAGE)); |
||||
assertThat(counts.get(0).getCount(), is(2L)); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue