ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 双表查询 ``` public class Post { private long id; private String title; private String content; @ManyToOne private User creator; ... } public class User { private long id; private String email; private String userName; ... } ``` 此时,我们知道,Post表中creator字段存储的是User的id。通常情况下,我们可以根据User对象来获取某个用户创建的所有博客: ``` List<Post> findByCreator(User user); ``` 但是在这种方式下,如果我们只知道用户的userName,我们就必须通过UserRepository先拿到User对象,再调用上述方法得到某个用户创建的所有博客。 实际上,我们通常会通过连表查询的方式一次性拿到相应的数据。在JPA里面,通常有下面两种方法: 通过方法名定义 我们可以通过如下语句去定义一个连表查询, ``` List<Post> findByCreatorUserName(String userName); ``` 此时,Spring Data JPA将按照以下顺序创建查询语句: 首先查找Post对象中是否有creatorUserName属性,如果有,则直接生成查询语句… where x.creatorUserName = ?1,我们的Post对象并没有该属性,进入下一步 查找是否含有creatorUser属性,如果有,则看creatorUser是否含有name属性,如果有,则生成相应的查询语句,我们的Post对象并没有该属性,进入下一步 查找是否有creator属性,如果依然没有,则抛出错误PropertyReferenceException,我们的Post对象存在该属性,进入下一步 查找creator是否含有userName属性,若没有,依然抛出PropertyReferenceException,我们的User对象存在该属性,进入下一步 由于creator存在userName属性,生成查询语句… where x.creator.userName = ?1 我们可以看到,属性表达式通过字母的大小写来与实体对象的属性进行关联。但是,如果在Post对象中恰巧有creatorUserName这个属性,那么,Spring Data JPA将会在第一步就完成查询语句的生成,但是,这并不是我们所希望的结果。此时,我们可以通过_来控制属性表达式: ``` List<Post> findByCreator_UserName(String userName); ``` 此时,Spring Data JPA的属性表达式将直接寻找creator属性下的userName属性。 通过@Query标注定义 在JPA中,实际上是可以自己写sql语句的,方法如下: ``` @Query("select * from post inner join user on post.creator = user.id where user.userName = ?1;", nativeQuery = true) List<Post> findByUser(String userName); ``` # 多表查询 ## 1.目的:记录如何 用jpa来实现快速的多表查询, 暂不深入探究jpa的内部实现 ## 2. 说明情形: @1所涉及的表为t_user(id,name,date,...);t_factory_user(id,factory_id,user_id,...), 其中表s_factory_user[user_id]==t_user[id], 为主外键的关联关系 @2现状 在entity类User中, 如下代码: ``` @OneToMany(mappedBy = "s_factory_user", fetch = FetchType.LAZY, cascade = {CascadeType.ALL}) private FactoryUser fu = new FactoryUser(); ``` 在entity类FactoryUser中, 如下代码: ``` @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH}) @JoinColumn(name = "user_id") private User user; ``` 添加代码后, 在启动项目后会报错, “org.hibernate.MappingException: Could not determine type for:”, 即为在不能在Use实体类中,定义属性fu(fu属性类型为factoryUser); 解决:在查询了相关文章后, “mappingexception”是指配置hibernate配置文件时错误或没有加载上hbm配置文件。现在该项目中用到的是jpa的注解方式来配置hibernate映射关系,还是我在属性的注解上有错误。 尝试直接在属性上添加属性@Transient, 加载依然报错,那么现在简单了解一下@OneToMany的使用; @1.其中的mappedBy指的是关联类FactoryUser中的属性名user @2. 在factoryUser类中,@JoinColumn中的name属性为‘userId’, 是对应的factoryUser的属性userId 则正常的应该为: 在factoryUser类中 ``` private User user; @OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH}) @JoinColumn(name = "userId") public User getUser() { return user; } ``` 在User类中 ``` private FactoryUser fu; @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user") public FactoryUser getFu() { return fu; } ``` ok, 这样项目可以正常启动了。 3.使用jpa的查询 @1 要集成接口类JpaSpecificationExecutor, 该类提供了几个方法 ``` T findOne(Specification<T> spec); List<T> findAll(Specification<T> spec); Page<T> findAll(Specification<T> spec, Pageable pageable); ``` @2 具体的使用 ``` <span> </span>Specification<User> spec = new Specification<User>() { @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Predicate p1 = cb.equal(root.get("userCategory").as(Integer.class), 2); // 设置sql链接 Join<User, FactoryUser> fuJoin = root.join(root.getModel().getSingularAttribute("fu", FactoryUser.class), JoinType.INNER); Predicate p2 = cb.equal(fuJoin.get("factoryId").as(Long.class), factoryIdFinal); query.where(cb.and(p1, p2)); // 添加排序的功能 query.orderBy(cb.desc(root.get("id").as(Long.class))); return query.getRestriction(); } }; return userDao.findAll(spec); ```