diff --git a/pom.xml b/pom.xml index 0709742..c652f36 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.darrachequesne spring-data-jpa-datatables - 2.2 + 2.3-SNAPSHOT Spring Data JPA for DataTables Spring Data JPA extension to work with the great jQuery plug-in DataTables (http://datatables.net/) 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 2fa5254..211b1ae 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,9 +7,11 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; +import javax.persistence.criteria.From; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import javax.persistence.metamodel.Attribute.PersistentAttributeType; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -193,10 +195,16 @@ private static Expression getExpression(Root root, String columnData) if (columnData.contains(ATTRIBUTE_SEPARATOR)) { // columnData is like "joinedEntity.attribute" so add a join clause String[] values = columnData.split("\\" + ATTRIBUTE_SEPARATOR); - if (!root.getModel().getAttribute(values[0]).isAssociation()) { + if (root.getModel().getAttribute(values[0]) + .getPersistentAttributeType() == PersistentAttributeType.EMBEDDED) { + // with @Embedded attribute return root.get(values[0]).get(values[1]).as(String.class); } - return root.join(values[0], JoinType.LEFT).get(values[1]).as(String.class); + From from = root; + for (int i = 0; i < values.length - 1; i++) { + from = from.join(values[i], JoinType.LEFT); + } + return from.get(values[values.length - 1]).as(String.class); } else { // columnData is like "attribute" so nothing particular to do return root.get(columnData).as(String.class); 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 98c414a..3daad8f 100644 --- a/src/test/java/org/springframework/data/jpa/datatables/Config.java +++ b/src/test/java/org/springframework/data/jpa/datatables/Config.java @@ -1,9 +1,12 @@ package org.springframework.data.jpa.datatables; import java.sql.SQLException; +import java.util.Properties; import javax.sql.DataSource; +import org.hibernate.cfg.Environment; +import org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.datatables.repository.DataTablesRepositoryFactoryBean; @@ -48,6 +51,13 @@ public AbstractEntityManagerFactoryBean entityManagerFactory() throws SQLExcepti bean.setPackagesToScan(Config.class.getPackage().getName()); bean.setDataSource(dataSource()); + Properties jpaProperties = new Properties(); + jpaProperties.setProperty(Environment.HBM2DDL_AUTO, "create-drop"); + jpaProperties.setProperty(Environment.HBM2DDL_IMPORT_FILES, "init.sql"); + jpaProperties.setProperty(Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR, + MultipleLinesSqlCommandExtractor.class.getName()); + bean.setJpaProperties(jpaProperties); + return bean; } } diff --git a/src/test/java/org/springframework/data/jpa/datatables/model/Course.java b/src/test/java/org/springframework/data/jpa/datatables/model/Course.java new file mode 100644 index 0000000..88bf53f --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/datatables/model/Course.java @@ -0,0 +1,47 @@ +package org.springframework.data.jpa.datatables.model; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "course") +public class Course { + + @Id + @GeneratedValue + private Long id; + + private String name; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "course_type_id") + private CourseType type; + + public Course() {} + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public CourseType getType() { + return type; + } + + public void setType(CourseType type) { + this.type = type; + } + +} diff --git a/src/test/java/org/springframework/data/jpa/datatables/model/CourseType.java b/src/test/java/org/springframework/data/jpa/datatables/model/CourseType.java new file mode 100644 index 0000000..3f9d959 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/datatables/model/CourseType.java @@ -0,0 +1,32 @@ +package org.springframework.data.jpa.datatables.model; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "course_type") +public class CourseType { + + @Id + @GeneratedValue + private Long id; + + private String name; + + public CourseType() {} + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/test/java/org/springframework/data/jpa/datatables/model/Lesson.java b/src/test/java/org/springframework/data/jpa/datatables/model/Lesson.java new file mode 100644 index 0000000..82202f2 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/datatables/model/Lesson.java @@ -0,0 +1,47 @@ +package org.springframework.data.jpa.datatables.model; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "lesson") +public class Lesson { + + @Id + @GeneratedValue + private Long id; + + private String name; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "course_id") + private Course course; + + public Lesson() {} + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Course getCourse() { + return course; + } + + public void setCourse(Course course) { + this.course = course; + } + +} diff --git a/src/test/java/org/springframework/data/jpa/datatables/model/LessonRepository.java b/src/test/java/org/springframework/data/jpa/datatables/model/LessonRepository.java new file mode 100644 index 0000000..71d1ed6 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/datatables/model/LessonRepository.java @@ -0,0 +1,7 @@ +package org.springframework.data.jpa.datatables.model; + +import org.springframework.data.jpa.datatables.repository.DataTablesRepository; + +public interface LessonRepository extends DataTablesRepository { + +} 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 new file mode 100644 index 0000000..9396280 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/datatables/repository/LessonRepositoryTest.java @@ -0,0 +1,74 @@ +package org.springframework.data.jpa.datatables.repository; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.ArrayList; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.datatables.Config; +import org.springframework.data.jpa.datatables.mapping.DataTablesInput; +import org.springframework.data.jpa.datatables.mapping.DataTablesOutput; +import org.springframework.data.jpa.datatables.model.Lesson; +import org.springframework.data.jpa.datatables.model.LessonRepository; +import org.springframework.data.jpa.datatables.parameter.ColumnParameter; +import org.springframework.data.jpa.datatables.parameter.OrderParameter; +import org.springframework.data.jpa.datatables.parameter.SearchParameter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = Config.class) +public class LessonRepositoryTest { + + @Autowired + private LessonRepository lessonRepository; + + @Test + public void testThroughTwoManyToOneRelationships() { + DataTablesInput input = getBasicInput(); + + input.getColumns().get(3).getSearch().setValue("CourseTypeA"); + DataTablesOutput output = lessonRepository.findAll(input); + assertNotNull(output); + assertNull(output.getError()); + assertEquals(5, (long) output.getRecordsFiltered()); + assertEquals(7, (long) output.getRecordsTotal()); + + input.getColumns().get(2).getSearch().setValue("CourseA-2"); + output = lessonRepository.findAll(input); + assertNotNull(output); + assertNull(output.getError()); + assertEquals(2, (long) output.getRecordsFiltered()); + assertEquals(7, (long) output.getRecordsTotal()); + } + + /** + * + * @return basic input parameters + */ + private static DataTablesInput getBasicInput() { + DataTablesInput input = new DataTablesInput(); + input.setDraw(1); + input.setStart(0); + input.setLength(10); + input.setSearch(new SearchParameter("", false)); + input.setOrder(new ArrayList()); + input.getOrder().add(new OrderParameter(0, "asc")); + + input.setColumns(new ArrayList()); + input.getColumns() + .add(new ColumnParameter("id", "", true, true, new SearchParameter("", false))); + input.getColumns() + .add(new ColumnParameter("name", "", true, true, new SearchParameter("", false))); + input.getColumns() + .add(new ColumnParameter("course.name", "", true, true, new SearchParameter("", false))); + input.getColumns().add( + new ColumnParameter("course.type.name", "", true, true, new SearchParameter("", false))); + + return input; + } +} diff --git a/src/test/resources/init.sql b/src/test/resources/init.sql new file mode 100644 index 0000000..740228b --- /dev/null +++ b/src/test/resources/init.sql @@ -0,0 +1,19 @@ +INSERT INTO course_type (id, name) VALUES + (1, 'CourseTypeA'), + (2, 'CourseTypeB'), + (3, 'CourseTypeC'); + +INSERT INTO course (id, name, course_type_id) VALUES + (1, 'CourseA-1', 1), + (2, 'CourseA-2', 1), + (3, 'CourseA-3', 1), + (4, 'CourseB-1', 2); + +INSERT INTO lesson (id, name, course_id) VALUES + (1, 'LessonA-1-a', 1), + (2, 'LessonA-1-b', 1), + (3, 'LessonA-1-c', 1), + (4, 'LessonA-2-a', 2), + (5, 'LessonA-2-b', 2), + (6, 'LessonB-1-a', 4), + (7, 'LessonB-1-b', 4);