diff --git a/pom.xml b/pom.xml index c652f36..6a76b9d 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,7 @@ 4.2.0.RELEASE 1.8.2.RELEASE - 4.3.10.Final + 4.3.11.Final 5.2.0.Final 2.6.0 2.2.5 diff --git a/src/main/java/org/springframework/data/jpa/datatables/repository/DataTablesUtils.java b/src/main/java/org/springframework/data/jpa/datatables/repository/DataTablesUtils.java index 211b1ae..bceb4cd 100644 --- a/src/main/java/org/springframework/data/jpa/datatables/repository/DataTablesUtils.java +++ b/src/main/java/org/springframework/data/jpa/datatables/repository/DataTablesUtils.java @@ -7,6 +7,7 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Fetch; import javax.persistence.criteria.From; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; @@ -102,6 +103,29 @@ public Predicate toPredicate(Root root, CriteriaQuery query, } predicate = criteriaBuilder.and(predicate, matchOneColumnPredicate); } + // findAll method does a count query first, and then query for the actual data. Yet in the + // count query, adding a JOIN FETCH results in the following error 'query specified join + // fetching, but the owner of the fetched association was not present in the select list' + // see https://jira.spring.io/browse/DATAJPA-105 + boolean isCountQuery = query.getResultType() == Long.class; + if (isCountQuery) { + return predicate; + } + // add JOIN FETCH when necessary + for (ColumnParameter column : input.getColumns()) { + if (!column.getSearchable() || !column.getData().contains(ATTRIBUTE_SEPARATOR)) { + continue; + } + String[] values = column.getData().split("\\" + ATTRIBUTE_SEPARATOR); + if (root.getModel().getAttribute(values[0]) + .getPersistentAttributeType() == PersistentAttributeType.EMBEDDED) { + continue; + } + Fetch fetch = null; + for (int i = 0; i < values.length - 1; i++) { + fetch = (fetch == null ? root : fetch).fetch(values[i], JoinType.LEFT); + } + } return predicate; } diff --git a/src/test/java/org/springframework/data/jpa/datatables/Config.java b/src/test/java/org/springframework/data/jpa/datatables/Config.java index 3daad8f..a1a8fbe 100644 --- a/src/test/java/org/springframework/data/jpa/datatables/Config.java +++ b/src/test/java/org/springframework/data/jpa/datatables/Config.java @@ -5,7 +5,9 @@ import javax.sql.DataSource; +import org.hibernate.SessionFactory; import org.hibernate.cfg.Environment; +import org.hibernate.jpa.HibernateEntityManagerFactory; import org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -60,4 +62,10 @@ public AbstractEntityManagerFactoryBean entityManagerFactory() throws SQLExcepti return bean; } + + @Bean + public SessionFactory sessionFactory() throws SQLException { + return ((HibernateEntityManagerFactory) entityManagerFactory().getObject()).getSessionFactory(); + } + } diff --git a/src/test/java/org/springframework/data/jpa/datatables/repository/LessonRepositoryTest.java b/src/test/java/org/springframework/data/jpa/datatables/repository/LessonRepositoryTest.java index 0425f7a..1aeee06 100644 --- a/src/test/java/org/springframework/data/jpa/datatables/repository/LessonRepositoryTest.java +++ b/src/test/java/org/springframework/data/jpa/datatables/repository/LessonRepositoryTest.java @@ -4,6 +4,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import org.hibernate.SessionFactory; +import org.hibernate.stat.Statistics; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +24,9 @@ public class LessonRepositoryTest { @Autowired private LessonRepository lessonRepository; + @Autowired + private SessionFactory sessionFactory; + @Test public void testThroughTwoManyToOneRelationships() { DataTablesInput input = getBasicInput(); @@ -41,6 +46,22 @@ public void testThroughTwoManyToOneRelationships() { assertEquals(7, output.getRecordsTotal()); } + @Test + public void testEagerLoading() { + DataTablesInput input = getBasicInput(); + + Statistics statistics = sessionFactory.getStatistics(); + statistics.setStatisticsEnabled(true); + DataTablesOutput output = lessonRepository.findAll(input); + assertEquals("CourseTypeA", output.getData().get(0).getCourse().getType().getName()); + statistics.setStatisticsEnabled(false); + + // there should be only three executed queries : count unfiltered, count filtered and actual + // data (with FETCH JOIN) + assertEquals(3, statistics.getPrepareStatementCount()); + assertEquals(7 + 3 + 2, statistics.getEntityLoadCount()); + } + /** * * @return basic input parameters diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties index 924db85..f0ad5b4 100644 --- a/src/test/resources/log4j.properties +++ b/src/test/resources/log4j.properties @@ -4,4 +4,5 @@ log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n log4j.appender.console=org.apache.log4j.ConsoleAppender -log4j.logger.org.springframework=INFO \ No newline at end of file +log4j.logger.org.springframework=INFO +log4j.logger.org.hibernate.SQL=DEBUG