è¿°èªã䜿çšãããšãããŒã¿ããŒã¹èŠçŽ ãéåžžã®ã¯ã©ã¹ãã£ãŒã«ããšããŠæäœã§ããŸãã ã¢ã»ã³ããªäžã«ãgradleã¯ç¹å¥ãªäŸåé¢ä¿ã¯ã©ã¹ãäœæãããããéããŠããŒã¿ããŒã¹ã§å¿ èŠãªã¬ã³ãŒããæ€çŽ¢ãããŸãã
æ¢ã«QueryDSLã§ã®äœæ¥ã«æåããŠãããèšäºã«å¯Ÿãã建èšçãªã³ã¡ã³ããææ¡ãããã°ãåãã§èªãã§ãå¿ èŠã«å¿ããŠèšäºã§è£è¶³ããŸãã
èšäºã®æåŸã«ã¯ããªããžããªãžã®ãªã³ã¯ããããããããäŸãã¯ããŒã³ïŒãŸãã¯ãã©ãŒã¯ïŒããããšãã§ããŸãã ç§ã¯æ£çŽã«ããªãã«èŠåããŸã-ç§ã¯ããŒã¹ã§ããããã¹ãããŸããã§ãããããããããªãã®ããã«äžæããïŒãããŠäžæããïŒãªããããã¯ééããªãåäœããŸãã ç§ããã¹ããæžãå§ããŸããããããªãã¯ééããªããã¹ããæžãã§ãããããããŠä»æ¥ç§ã¯èšäºã®ãããã¯-è¿°èªãåæããããšæããŸãã
é£æºãããšã³ãã£ãã£ãäœæããŸãã AbstractEntityããç¶æ¿ããããã£ãŒã«ãåãšå¹Žéœ¢ããã³UserGroupãæã€ãŠãŒã¶ãŒãšããŸãã ãããã®éã«1察å€ã®é¢ä¿ãäœæããŸããã-1ã€ã®ã°ã«ãŒãã«å€ãã®ãŠãŒã¶ãŒãååšããå¯èœæ§ããããŸãã è¿°èªã¯ããŠãŒã¶ãŒã®ã¿ã«ãã£ãŠè§£æãããŸãã
AbstractEntityïŒ
package entity; import javax.persistence.*; @MappedSuperclass public class AbstractEntity { private Long id; @Id @Column(name = "id") @SequenceGenerator(name = "general_seq", sequenceName = "generalSequenceGenerator") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "general_seq") public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AbstractEntity that = (AbstractEntity) o; return id != null ? id.equals(that.id) : that.id == null; } @Override public int hashCode() { return id != null ? id.hashCode() : 0; } }
ãŠãŒã¶ãŒïŒ
package entity; import javax.persistence.*; @Entity @Table(name = "users") public class User extends AbstractEntity { private String name; private Integer age; private UserGroup group; @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(name = "age") public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "category") public UserGroup getGroup() { return group; } public void setGroup(UserGroup group) { this.group = group; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; User user = (User) o; if (name != null ? !name.equals(user.name) : user.name != null) return false; if (age != null ? !age.equals(user.age) : user.age != null) return false; return group != null ? group.equals(user.group) : user.group == null; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (name != null ? name.hashCode() : 0); result = 31 * result + (age != null ? age.hashCode() : 0); result = 31 * result + (group != null ? group.hashCode() : 0); return result; } }
ãŠãŒã¶ãŒã°ã«ãŒãïŒ
package entity; import javax.persistence.*; import java.util.List; @Entity @Table(name = "user_groups") public class UserGroup extends AbstractEntity { private String name; private List<User> users; @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "group") public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; UserGroup userGroup = (UserGroup) o; if (name != null ? !name.equals(userGroup.name) : userGroup.name != null) return false; return users != null ? users.equals(userGroup.users) : userGroup.users == null; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (name != null ? name.hashCode() : 0); result = 31 * result + (users != null ? users.hashCode() : 0); return result; } }
ããŠããšã³ãã£ãã£ããããŸããä»åºŠã¯ããªããžããªãæäœããã®ã«å¿ èŠãªãã®ã«ã€ããŠèããŠã¿ãŸãããã ã§ããããšïŒ
- å¿ èŠãªå¹Žéœ¢å¶éå ã®ãã¹ãŠã®ãŠãŒã¶ãŒãæ€çŽ¢ããŸãïŒå å«ãšé€å€ã®äž¡æ¹ïŒã
- IDã§ãŠãŒã¶ãŒãæ€çŽ¢ããŸãã
- ç¹å®ã®ã°ã«ãŒãã®ãã¹ãŠã®ãŠãŒã¶ãŒãšãè€æ°ã®ã°ã«ãŒãã®ãŠãŒã¶ãŒãæ€çŽ¢ããŸãã
- ãŠãŒã¶ãŒãååã§æ€çŽ¢ããŸãã
...ããŒãºãšãã£ãŒã«ãã®æ°ã«å¿ããŠããŠãŒã¶ãŒã奜ããªããã«æ€çŽ¢ããœãŒãããã£ã«ã¿ãªã³ã°ããŸãã
QueryDSLãä»ããŠããŒã¿ããŒã¹ãæäœããã«ã¯ãåå¥ã®ã«ã¹ã¿ãã€ãºããããªããžããªãå¿ èŠã§ãã Spring JPAãä»ããŠããŒã¿ããŒã¹ãæäœããä»ã®å Žåãšåãæ¹æ³ã§JpaRepositoryããæ¡åŒµãããŸãããQueryDSLã§ã«ã¹ã¿ãã€ãºãããŸãã
@NoRepositoryBean public interface ExCustomRepository<T extends AbstractEntity, P extends EntityPathBase<T>, ID extends Serializable> extends JpaRepository<T, ID>, QuerydslPredicateExecutor<T>, QuerydslBinderCustomizer<P> { @Override default void customize(QuerydslBindings bindings, P root) { } }
éèŠãªãç¥ããã org.springframework.data.mapping.PropertyReferenceExceptionïŒ Springãäžããããšãããšãã«Userã¿ã€ãã®ããããã£ã«ã¹ã¿ãã€ãºãèŠã€ãããªãå Žåã¯ã CustomizeïŒïŒã¡ãœãããå®è£ ããŠããŸããã åå®çŸ©ããã ãã§ååã§ãïŒã«ã¹ã¿ãã€ãºããããªãå ŽåïŒã
ãããã£ãŠããªããžããªãæäœããã«ã¯ãUserãQUserãããã³Longãæ瀺çã«æå®ããŠã次ã®ããã«ExCustomRepositoryããUserRepositoryã€ã³ã¿ãŒãã§ã€ã¹ãç¶æ¿ããã ãã§ååã§ãã
@Repository public interface UserRepository extends ExCustomRepository<User, QUser, Long> { }
æåŸã«ãããŒã¿ããŒã¹ã«ã¢ã¯ã»ã¹ããŠãŠãŒã¶ãŒãæ€çŽ¢ãããµãŒãã¹ã¯ã©ã¹ãäœæããŸãã 空ã®éã«ã
@Service public class UserService { @Autowired UserRepository repository; // , public List<User> getByAgeExcluding(Integer minAge, Integer maxAge) { } // , public List<User> getByAgeIncluding(Integer minAge, Integer maxAge) { } // ID public User getById(Long id) { } // public List<User> getByGroups(List<UserGroup> groups) { } // public List<User> get(String name) { } }
SpringãããŒãã«ãšã³ãã£ãã£ã®ãªããžã§ã¯ãè¡šçŸãæäœããã«ã¯ããããã®éã®é¢ä¿ãäœæããå¿ èŠããããŸãã ããã©ã«ãã§ã¯ããã¹ãŠã®ãªã³ã¯ãbuild.generated.source.apt.project_structureãã©ã«ããŒã«é 眮ããŸãããããã®ãªã³ã¯ãäœæããã«ã¯ããããžã§ã¯ããã¯ãªã¢ããŠã¯ã©ã¹ãåéããå¿ èŠããããŸãã gradleã§ã¯ãããã¯cleanã¿ã¹ã¯ãšclassesã¿ã¹ã¯ãé 次å®è¡ããããšã§å®çŸãããŸãïŒgradle-> Tasks-> build-> cleanãclassesïŒã ãããžã§ã¯ãæ§é ãbuild.generated.source.aptã«è¡šç€ºãããQãã¬ãã£ãã¯ã¹ãä»ããã¯ã©ã¹ã衚瀺ãããŠããå Žåããã¹ãŠãæ£ããå®è¡ããŸããã
ãã¹ãŠãæ£ããè¡ããäžèšã®ã¯ã©ã¹ã衚瀺ããããšããŸãã å¿ èŠã«å¿ããŠãããšãã°18ã60幎ãªã©ããã¹ãŠã®ãŠãŒã¶ãŒããªããžããªãããªã¯ãšã¹ãããŸãããã æ¢ã«è¿°ã¹ãããã«ãQueryDSLã®ããŒãã«ãšã³ãã£ãã£éã®é¢ä¿ã¯ãQãã¬ãã£ãã¯ã¹ãæã€å¯Ÿå¿ããã¯ã©ã¹ã§åœ¢æãããŸããUserã¯ã©ã¹ã®å Žåãããã¯QUserã«ãªããŸãã QUserã¯ãªããžããªå šäœã§ãã ãŠãŒã¶ãŒïŒ QUser.user ããŠãŒã¶ãŒã®ååïŒ QUser.user.name ãããã³ãã®å Žåãå¹Žéœ¢ïŒ QUser.user.ageããããŸãã ãããã£ãŠã幎霢ãååŸããããã«ã QUser.user.ageã䜿çšããŸãã
QueryDSLã«ã¯ãç¹å®ã®çµæãçæã§ãã4ã€ã®äž»èŠãªã¡ãœããããããŸãã
- findOneïŒïŒ-ä»»æã®1ã€ã®èŠçŽ ãæ€çŽ¢ã§ããŸãã ããŒã¿ããŒã¹ã«å¿ èŠãªã¢ã€ãã ã1ã€ãããªãããšã確èªããå¿ èŠããããŸããããã§ãªãå ŽåãèŠãçŒãã¯äŸå€ã§ãã
- findAllïŒïŒããã³ãã®ããã€ãã®ãªãŒããŒããŒã-æ¡ä»¶ãæºããã¬ã³ãŒãã®å埩å¯èœãªãªã¹ããè¿ããŸãã éåžžããã®ãªã¹ãã¯ãªã¹ãã«ã©ããããå¿ èŠããããŸãïŒãªã¹ãããããŸãïŒã
- countïŒïŒ-èŠã€ãã£ãã¢ã€ãã ã®æ°ãè¿ããŸãã
- existsïŒïŒ-ãã®ãããªèŠçŽ ãããŒãã«ã«ååšãããã©ããã«ããããããããŒã«å€ãè¿ããŸãã
ãããã®ã¡ãœããã¯ãorg.springframework.data.querydsl.QuerydslPredicateExecutorã§èŠã€ããããšãã§ããŸãã ããã±ãŒãžåãããããããã«ãSpringã¯ããããæäŸããŸãã
èŠçŽ ã®æ¡ä»¶ãæºããããã«ãcom.querydsl.core.types.dsl.SimpleExpressionã«ç¡æ°ã®ã¡ãœãããä¿åãããŠããŸãã ããããããã«è©³ãã調ã¹ãŸãã
ãããã£ãŠãæåã®æ¹æ³ã§ã¯ãç¹å®ã®å¹Žéœ¢å±€ã®ãã¹ãŠã®ãŠãŒã¶ãŒãååŸããå¿ èŠããããŸãã
HQLã§ã¯ããã®ã¯ãšãªã¯æ¬¡ã®ããã«ãªããŸãã
SELECT u FROM User u WHERE u.age BETWEEN :minAge AND :maxAge
QueryDSLã®å®è£ ã§ã¯ããã®ã¡ãœããã¯æ¬¡ã®ããã«ãªããŸãã
public List<User> getByAgeExcluding(Integer minAge, Integer maxAge) { return Lists.newArrayList(repository.findAll(QUser.user.age.between(minAge, maxAge))); }
å¹Žéœ¢ïŒ QUser.user.age ïŒãæå®ãããç¯å²ïŒïŒminAgeãmaxAgeïŒã®éïŒã«ãããã¹ãŠã®ãŠãŒã¶ãŒïŒfindAllïŒïŒïŒãæ¢ããŠããŸãã ãããŠãããããã¹ãŠã®ãªã¯ãšã¹ãã§ãã ãã®ãªã¯ãšã¹ãã«å¿ããŠããŠãŒã¶ãŒã®æ¢è£œãªã¹ããååŸããŸãã SQLã¯ãšãªãèšè¿°ããå¿ èŠã¯ãããŸããããããããªå€æŽã§å床æžãçŽããšããã¹ãŠã倱æããŸããQueryDSLã¯ããšã³ãã£ãã£ã®ãªããžã§ã¯ãéä¿¡ãæäŸã§ããæ倧éã®æè»æ§ãæäŸããŸãã
ããã¯ããããªäœè«ã§ãããããŸã 4ã€ã®å®£èšãããäŸããããŸãã 次ã®è©±ã«ç§»ããŸãããã ç¯å²å ã®ãã¹ãŠã®ãŠãŒã¶ãŒãèŠã€ãããŸããããéžæãããšå¢çå€èªäœãé€å€ãããŸãã æ€çŽ¢æ¡ä»¶ã«å¢çå€ãå«ããã«ã¯ãä»ã®æ¹æ³ã䜿çšããå¿ èŠããããŸãã
- goe-以äžïŒä»¥äžïŒ
- loe-ããå°ãããçãã
ååŸãããªã¯ãšã¹ãã¯æ¬¡ã®ãšããã§ãã
public List<User> getByAgeIncluding(Integer minAge, Integer maxAge) { return Lists.newArrayList(repository.findAll(QUser.user.age.goe(minAge).and(QUser.user.age.loe(maxAge)))); }
ãã®èŠæ±ãæºããã«ã¯ã2ã€ã®æ¡ä»¶ã䜿çšããå¿ èŠããããŸãã ãããè¡ãã«ã¯ãandïŒïŒãã€ã³ãã£ã³ã°ã¡ãœããã䜿çšããŸãããã®ã¡ãœããã¯ããã®ããã«é¢é£ãããã¹ãŠã®æ¡ä»¶ã§ãã£ã«ã¿ãŒåŠçããŸãã ãã¬ãŒã ã¯ãŒã¯ã¯ãæåã«minAge以äžã®ãã¹ãŠã®ãªããžã§ã¯ããéžæãã次ã«maxAge以äžã®ãã¹ãŠã®ãªããžã§ã¯ããéžæãããã¹ãŠã1ã€ã®èŠæ±ã§éžæããŸãã ãã®ãããªãã³ãã«ã¯å€æ°ååšããå¯èœæ§ããããŸãããorïŒïŒãªã©ã®æãããããããã¯com.querydsl.core.types.dsl.BooleanExpressionã§èŠã€ããããšãã§ããŸãã
次ã«ãIDã§ãŠãŒã¶ãŒãèŠã€ããŸãããã ãã¡ãããããã¯é©åãªSpring JPAã¡ãœããfindByIdïŒïŒã䜿çšããŠè¡ãã®ãæé©ã§ãããQueryDSLã解æããŠããããã察å¿ããã¯ãšãªãäœæããŸãã
public User getById(Long id) { return repository.findOne(QUser.user.id.eq(id)).orElse(new User()); }
eqïŒïŒæŒç®åã䜿çšããŸããããã¯ãæ¡ä»¶ã«çãããã£ãŒã«ãã§æ€çŽ¢ããŸãïŒeq = equalsïŒã
ç¶ããŸãããã ç§ãã¡ã®å Žåãã°ã«ãŒãå ã®ãã¹ãŠã®ãŠãŒã¶ãŒãèŠã€ããããã«ãäœãæ€çŽ¢ããå¿ èŠãããããŸãã-å¿ èŠãªUserGroupããã®Listãã£ãŒã«ãã§ååŸããè€æ°ã®ã°ã«ãŒãå ã®ãã¹ãŠã®ãŠãŒã¶ãŒãèŠã€ããå¿ èŠãããå Žå ãããŠããã®ã¿ã¹ã¯ã¯ãQueryDSLãä»ããéåžžã«ç°¡åãªã¯ãšãªã§å®çŸã§ããŸãã
public List<User> getByGroups(List<UserGroup> groups) { return Lists.newArrayList(repository.findAll(QUser.user.group.in(groups))); }
ãã®å ŽåãïŒïŒã®æŒç®åã䜿çšãããšã1ã€ã®å€ã§ã¯ãªãè€æ°ã®æ€çŽ¢æ¡ä»¶ãèšå®ã§ããŸãã
ãããŠæåŸã«ãèŠæ±ãããååãšã¯ç°ãªãååãæã€ãã¹ãŠã®ãŠãŒã¶ãŒãèŠã€ããŸãïŒããšãã°ããã¹ãŠã®éIvanovsïŒã ãªã¯ãšã¹ãã¯æ¬¡ã®ããã«ãªããŸãã
public List<User> get(String name) { return Lists.newArrayList(repository.findAll(QUser.user.name.ne(name))); }
ãã®ã¯ãšãªã®ãããã§ãIvana以å€ã®ãã¹ãŠãèŠã€ãããŸãã
ãã®èšäºã§ã¯ã5ã€ã®ç°ãªãQueryDSLã¯ãšãªãåãäžããŸããã å®éã®ãããžã§ã¯ãã§ã¯ãããªãšãŒã·ã§ã³ã®æ°ã¯ãšã³ãã£ãã£ãã£ãŒã«ãã®æ°ãšãããã®éã®é¢ä¿ã«ãã£ãŠã®ã¿å¶éã§ããŸãã QueryDSLã¯éåžžã«åŒ·åã§ãããšåæã«ãéåžžã«ç解ããããJavaããã°ã©ããŒãã¬ãŒã ã¯ãŒã¯ã§ãã ãããç 究ããåŸãããªãã¯ããªãèªèº«ã®ã³ãŒããšåãæ¹æ³ã§ããŒã¿ããŒã¹ãæ±ãã®ã倧奜ãã§ã:)
githubã§ã®çŽæãããäŸã