From 387959d96c36c28380b1a1ac067c19a958862f2a Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Mon, 2 Jun 2014 13:38:30 +0200 Subject: [PATCH] DATAJPA-527 - Improved handling of CrudRepository.exists(ID) entities with complex composite id. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now delegate the exists(…) check to findOne(…) in SimpleJpaRepository for entities that have a complex composite id via @IdClass. Previously we tried to generate a string based count query in that case, which didn't work since the parameter types for the IdClass attributes didn't match the values returned by entityInformation.getCompositeIdAttributeValue(…). Polished JavaDoc in JpaEntityInformation. Original pull request: #95. --- .../support/JpaEntityInformation.java | 6 +-- .../support/SimpleJpaRepository.java | 50 ++++++++++++------- .../RepositoryWithCompositeKeyTests.java | 48 ++++++++++++++++++ .../support/JpaRepositoryTests.java | 23 +++++++-- 4 files changed, 102 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java index b221f8875..1a2e3aa9c 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2014 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. @@ -22,7 +22,7 @@ import javax.persistence.metamodel.SingularAttribute; import org.springframework.data.repository.core.EntityInformation; /** - * Extension of {@link EntityInformation} to capture aditional JPA specific information about entities. + * Extension of {@link EntityInformation} to capture additional JPA specific information about entities. * * @author Oliver Gierke * @author Thomas Darimont @@ -38,7 +38,7 @@ public interface JpaEntityInformation extends Entity SingularAttribute getIdAttribute(); /** - * Returns {@literal true} if the entity has a composite id + * Returns {@literal true} if the entity has a composite id. * * @return */ diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index c68c492ce..67c9b2a98 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -241,27 +241,39 @@ public class SimpleJpaRepository implements JpaRepos Assert.notNull(id, "The given id must not be null!"); - if (entityInformation.getIdAttribute() != null) { - - String placeholder = provider.getCountQueryPlaceholder(); - String entityName = entityInformation.getEntityName(); - Iterable idAttributeNames = entityInformation.getIdAttributeNames(); - String existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames); - - TypedQuery query = em.createQuery(existsQuery, Long.class); - - if (entityInformation.hasCompositeId()) { - for (String idAttributeName : idAttributeNames) { - query.setParameter(idAttributeName, entityInformation.getCompositeIdAttributeValue(id, idAttributeName)); - } - } else { - query.setParameter(idAttributeNames.iterator().next(), id); - } - - return query.getSingleResult() == 1L; - } else { + if (entityInformation.getIdAttribute() == null) { return findOne(id) != null; } + + String placeholder = provider.getCountQueryPlaceholder(); + String entityName = entityInformation.getEntityName(); + Iterable idAttributeNames = entityInformation.getIdAttributeNames(); + String existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames); + + TypedQuery query = em.createQuery(existsQuery, Long.class); + + if (!entityInformation.hasCompositeId()) { + query.setParameter(idAttributeNames.iterator().next(), id); + return query.getSingleResult() == 1L; + } + + for (String idAttributeName : idAttributeNames) { + + Object idAttributeValue = entityInformation.getCompositeIdAttributeValue(id, idAttributeName); + + boolean complexIdParameterValueDiscovered = idAttributeValue != null + && !query.getParameter(idAttributeName).getParameterType().isAssignableFrom(idAttributeValue.getClass()); + + if (complexIdParameterValueDiscovered) { + + // fall-back to findOne(id) which does the proper mapping for the parameter. + return findOne(id) != null; + } + + query.setParameter(idAttributeName, idAttributeValue); + } + + return query.getSingleResult() == 1L; } /* diff --git a/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index 6fd98addf..2871859cf 100644 --- a/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -215,4 +215,52 @@ public class RepositoryWithCompositeKeyTests { assertThat(result.get(0), is(emp3)); assertThat(result.get(1), is(emp1)); } + + /** + * @see DATAJPA-527 + */ + @Test + public void testExistsWithIdClass() { + + IdClassExampleDepartment dep = new IdClassExampleDepartment(); + dep.setName("TestDepartment"); + dep.setDepartmentId(-1); + + IdClassExampleEmployee emp = new IdClassExampleEmployee(); + emp.setDepartment(dep); + + employeeRepositoryWithIdClass.save(emp); + + IdClassExampleEmployeePK key = new IdClassExampleEmployeePK(); + key.setDepartment(dep.getDepartmentId()); + key.setEmpId(emp.getEmpId()); + + assertThat(employeeRepositoryWithIdClass.exists(key), is(true)); + } + + /** + * @see DATAJPA-527 + */ + @Test + public void testExistsWithEmbeddedId() { + + EmbeddedIdExampleDepartment dep1 = new EmbeddedIdExampleDepartment(); + dep1.setDepartmentId(1L); + dep1.setName("Dep1"); + + EmbeddedIdExampleEmployeePK key = new EmbeddedIdExampleEmployeePK(); + key.setDepartmentId(1L); + key.setEmployeeId(1L); + + EmbeddedIdExampleEmployee emp = new EmbeddedIdExampleEmployee(); + emp.setDepartment(dep1); + emp.setEmployeePk(key); + + emp = employeeRepositoryWithEmbeddedId.save(emp); + + key.setDepartmentId(emp.getDepartment().getDepartmentId()); + key.setEmployeeId(emp.getEmployeePk().getEmployeeId()); + + assertThat(employeeRepositoryWithEmbeddedId.exists(key), is(true)); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java index c5eafef1e..b827d9ea9 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2013 the original author or authors. + * Copyright 2008-2014 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. @@ -40,14 +40,14 @@ import org.springframework.transaction.annotation.Transactional; * Integration test for {@link JpaRepository}. * * @author Oliver Gierke + * @author Thomas Darimont */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @Transactional public class JpaRepositoryTests { - @PersistenceContext - EntityManager em; + @PersistenceContext EntityManager em; JpaRepository repository; CrudRepository idClassRepository; @@ -104,6 +104,23 @@ public class JpaRepositoryTests { assertThat(idClassRepository.exists(new SampleWithIdClassPK(1L, 2L)), is(false)); } + /** + * @see DATAJPA-527 + */ + @Test + public void executesExistsForEntityWithIdClass() { + + SampleWithIdClass entity = new SampleWithIdClass(1L, 1L); + idClassRepository.save(entity); + + assertThat(entity.getFirst(), is(notNullValue())); + assertThat(entity.getSecond(), is(notNullValue())); + + SampleWithIdClassPK id = new SampleWithIdClassPK(entity.getFirst(), entity.getSecond()); + + assertThat(idClassRepository.exists(id), is(true)); + } + private static interface SampleEntityRepository extends JpaRepository { }