jpa中::::_项目学生:JPA标准查询
jpa中::::
這是Project Student的一部分。 其他職位包括帶有Jersey的Webservice Client,帶有Jersey的 Webservice Server , 業務層 , 具有Spring Data的持久性 ,分片集成測試數據和Webservice Integration 。
我們已經介紹了CRUD的基本操作,但是并沒有花太多時間。 Spring Data使包含基本搜索變得容易,但是擁有其他標準選項也很重要。 JPA標準查詢是最重要的查詢之一。
Spring Data JPA教程– JPA Criteria Queries [http://www.petrikainulainen.net]是對該材料的很好介紹。
設計決策
JPA標準 –我使用的是JPA標準搜索,而不是querydsl。 稍后我將返回querydsl。
局限性
違反封裝 –此設計需要打破使每個層完全不了解其他層的實現細節的體系結構目標。 這是一個非常小的違反行為-僅JPA規范-我們仍然必須處理分頁。 將它們放在一起,我覺得此時過分擔心這個還為時過早。
Web服務 -不更新web服務客戶端和服務器。 同樣,我們仍然必須處理分頁,并且無論如何我們現在要做的任何事情都必須更改。
重構先前的工作
我對以前的工作有三點改變。
findCoursesByTestRun()
我已經定義了方法:
List findCoursesByTestRun(TestRun testRun);在課程庫中。 那沒有預期的效果。 我需要的是
List findCoursesByTestRun_Uuid(String uuid);對調用代碼進行適當的更改。 或者,您可以只使用下面討論的JPA條件查詢。
FinderService和ManagerService
這來自Jumpstart網站上的研究。 作者將標準服務接口分為兩部分:
- FinderService –只讀操作(搜索)
- ManagerService –讀寫操作(創建,更新,刪除)
這很有道理,例如,當我們可以在類與方法級別上進行操作時,通過AOP添加行為會容易得多。 我在現有代碼中做了適當的更改。
查找錯誤
我已經修復了FindBugs發現的許多問題。
元數據
我們首先允許對持久對象的元數據訪問。 這使我們能夠創建可以通過JPA實現進行優化的查詢。
import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.StaticMetamodel;@StaticMetamodel(Course.class) public class Course_ {public static volatile SingularAttribute<Course, TestRun> testRun; }和
@StaticMetamodel(TestRun.class) public class TestRun_ {public static volatile SingularAttribute<TestRun, String> uuid; }由于約定優于配置,因此需要類的名稱。
- 有關此功能的討論,請參見靜態元數據 [jboss.org]。
技術指標
現在,我們可以使用現在可用的元數據創建查詢規范。 這是一個稍微復雜的查詢,因為我們需要深入研究結構。 (這不需要實際的聯接,因為testrun uuid用作外鍵。)
public class CourseSpecifications {/*** Creates a specification used to find courses with the specified testUuid.* * @param testRun* @return*/public static Specification<Course> testRunIs(final TestRun testRun) {return new Specification<Course>() {@Overridepublic Predicate toPredicate(Root<Course> courseRoot, CriteriaQuery<?> query, CriteriaBuilder cb) {Predicate p = null;if (testRun == null || testRun.getUuid() == null) {p = cb.isNull(courseRoot.<Course_> get("testRun"));} else {p = cb.equal(courseRoot.<Course_> get("testRun").<TestRun_> get("uuid"), testRun.getUuid());}return p;}};} }一些文檔建議我可以使用get(Course_.testRun)代替get(“ testRun”),但是eclipse在get()方法上將其標記為類型沖突。 你的旅費可能會改變。
Spring數據倉庫
我們必須告訴Spring Data我們正在使用JPA Criteria查詢。 這是通過擴展JpaSpecificationExecutor接口來完成的。
@Repository public interface CourseRepository extends JpaRepository<Course, Integer>,JpaSpecificationExecutor<Course> {Course findCourseByUuid(String uuid);List findCoursesByTestRunUuid(String uuid); }FinderService實施
現在,我們可以在我們的服務實現中使用JPA規范。 如上所述,使用JPA Criteria Specification違反了封裝。
import static com.invariantproperties.sandbox.student.specification.CourseSpecifications.testRunIs;@Service public class CourseFinderServiceImpl implements CourseFinderService {@Resourceprivate CourseRepository courseRepository;/*** @see com.invariantproperties.sandbox.student.business.FinderService#* count()*/@Transactional(readOnly = true)@Overridepublic long count() {return countByTestRun(null);}/*** @see com.invariantproperties.sandbox.student.business.FinderService#* countByTestRun(com.invariantproperties.sandbox.student.domain.TestRun)*/@Transactional(readOnly = true)@Overridepublic long countByTestRun(TestRun testRun) {long count = 0;try {count = courseRepository.count(testRunIs(testRun));} catch (DataAccessException e) {if (!(e instanceof UnitTestException)) {log.info("internal error retrieving classroom count by " + testRun, e);}throw new PersistenceException("unable to count classrooms by " + testRun, e, 0);}return count;}/*** @see com.invariantproperties.sandbox.student.business.CourseFinderService#* findAllCourses()*/@Transactional(readOnly = true)@Overridepublic List<Course> findAllCourses() {return findCoursesByTestRun(null);}/*** @see com.invariantproperties.sandbox.student.business.CourseFinderService#* findCoursesByTestRun(java.lang.String)*/@Transactional(readOnly = true)@Overridepublic List<Course> findCoursesByTestRun(TestRun testRun) {List<Course> courses = null;try {courses = courseRepository.findAll(testRunIs(testRun));} catch (DataAccessException e) {if (!(e instanceof UnitTestException)) {log.info("error loading list of courses: " + e.getMessage(), e);}throw new PersistenceException("unable to get list of courses.", e);}return courses;}.... }單元測試
我們的單元測試需要稍做更改才能使用規范。
public class CourseFinderServiceImplTest {private final Class<Specification<Course>> sClass = null;@Testpublic void testCount() {final long expected = 3;final CourseRepository repository = Mockito.mock(CourseRepository.class);when(repository.count(any(sClass))).thenReturn(expected);final CourseFinderService service = new CourseFinderServiceImpl(repository);final long actual = service.count();assertEquals(expected, actual);}@Testpublic void testCountByTestRun() {final long expected = 3;final TestRun testRun = new TestRun();final CourseRepository repository = Mockito.mock(CourseRepository.class);when(repository.count(any(sClass))).thenReturn(expected);final CourseFinderService service = new CourseFinderServiceImpl(repository);final long actual = service.countByTestRun(testRun);assertEquals(expected, actual);}@Test(expected = PersistenceException.class)public void testCountError() {final CourseRepository repository = Mockito.mock(CourseRepository.class);when(repository.count(any(sClass))).thenThrow(new UnitTestException());final CourseFinderService service = new CourseFinderServiceImpl(repository);service.count();}@Testpublic void testFindAllCourses() {final List<Course> expected = Collections.emptyList();final CourseRepository repository = Mockito.mock(CourseRepository.class);when(repository.findAll(any(sClass))).thenReturn(expected);final CourseFinderService service = new CourseFinderServiceImpl(repository);final List<Course> actual = service.findAllCourses();assertEquals(expected, actual);}@Test(expected = PersistenceException.class)public void testFindAllCoursesError() {final CourseRepository repository = Mockito.mock(CourseRepository.class);final Class<Specification<Course>> sClass = null;when(repository.findAll(any(sClass))).thenThrow(new UnitTestException());final CourseFinderService service = new CourseFinderServiceImpl(repository);service.findAllCourses();}@Testpublic void testFindCourseByTestUuid() {final TestRun testRun = new TestRun();final Course course = new Course();final List<Course> expected = Collections.singletonList(course);final CourseRepository repository = Mockito.mock(CourseRepository.class);when(repository.findAll(any(sClass))).thenReturn(expected);final CourseFinderService service = new CourseFinderServiceImpl(repository);final List actual = service.findCoursesByTestRun(testRun);assertEquals(expected, actual);}@Test(expected = PersistenceException.class)public void testFindCourseByTestUuidError() {final TestRun testRun = new TestRun();final CourseRepository repository = Mockito.mock(CourseRepository.class);when(repository.findAll(any(sClass))).thenThrow(new UnitTestException());final CourseFinderService service = new CourseFinderServiceImpl(repository);service.findCoursesByTestRun(testRun);}@Testpublic void testFindCoursesByTestUuid() {final TestRun testRun = new TestRun();final Course course = new Course();final List<Course> expected = Collections.singletonList(course);final CourseRepository repository = Mockito.mock(CourseRepository.class);when(repository.findAll(any(sClass))).thenReturn(expected);final CourseFinderService service = new CourseFinderServiceImpl(repository);final List<Course> actual = service.findCoursesByTestRun(testRun);assertEquals(expected, actual);}@Test(expected = PersistenceException.class)public void testFindCoursesByTestUuidError() {final TestRun testRun = new TestRun();final CourseRepository repository = Mockito.mock(CourseRepository.class);when(repository.findAll(any(sClass))).thenThrow(new UnitTestException());final CourseFinderService service = new CourseFinderServiceImpl(repository);service.findCoursesByTestRun(testRun);}.... }通過使用@Begin方法,我可以消除很多重復的代碼,但是我決定反對它來支持并行測試。
整合測試
我們終于來進行集成測試。 我們知道我們做對了,因為唯一的一行可以測試附加功能-計算數據庫中的課程數量。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { BusinessApplicationContext.class, TestBusinessApplicationContext.class,TestPersistenceJpaConfig.class }) @Transactional @TransactionConfiguration(defaultRollback = true) public class CourseServiceIntegrationTest {@Resourceprivate CourseFinderService fdao;@Resourceprivate CourseManagerService mdao;@Resourceprivate TestRunService testService;@Testpublic void testCourseLifecycle() throws Exception {final TestRun testRun = testService.createTestRun();final String name = "Calculus 101 : " + testRun.getUuid();final Course expected = new Course();expected.setName(name);assertNull(expected.getId());// create courseCourse actual = mdao.createCourseForTesting(name, testRun);expected.setId(actual.getId());expected.setUuid(actual.getUuid());expected.setCreationDate(actual.getCreationDate());assertThat(expected, equalTo(actual));assertNotNull(actual.getUuid());assertNotNull(actual.getCreationDate());// get course by idactual = fdao.findCourseById(expected.getId());assertThat(expected, equalTo(actual));// get course by uuidactual = fdao.findCourseByUuid(expected.getUuid());assertThat(expected, equalTo(actual));// get all coursesfinal List<Course> courses = fdao.findCoursesByTestRun(testRun);assertTrue(courses.contains(actual));// count coursesfinal long count = fdao.countByTestRun(testRun);assertTrue(count > 0);// update courseexpected.setName("Calculus 102 : " + testRun.getUuid());actual = mdao.updateCourse(actual, expected.getName());assertThat(expected, equalTo(actual));// delete Coursemdao.deleteCourse(expected.getUuid(), 0);try {fdao.findCourseByUuid(expected.getUuid());fail("exception expected");} catch (ObjectNotFoundException e) {// expected}testService.deleteTestRun(testRun.getUuid());} }源代碼
- 可從http://code.google.com/p/invariant-properties-blog/source/browse/student獲取源代碼。
翻譯自: https://www.javacodegeeks.com/2014/01/project-student-jpa-criteria-queries.html
jpa中::::
總結
以上是生活随笔為你收集整理的jpa中::::_项目学生:JPA标准查询的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 花英语怎么读 花英语是什么
- 下一篇: 具有ELK的APIGEE API网关日志