SpringBoot第八講Spring Data JPA的關聯(lián)對象查詢

現(xiàn)在關于SpringDataJpa我們總算只剩下一個比較重要的問題了,就是當有關聯(lián)對象的時候我們需要把關聯(lián)的對象查詢出來,該如何處理,如果在類的關聯(lián)中是基于對象關聯(lián)的,那么Hibernate的抓取策略可以自動完成,但是這種操作會存在各種性能上的問題,所以不建議用關聯(lián)的方式處理,我們僅僅只是做個外鍵關聯(lián),在實際的應用中,我們可以在Student中存儲班級的冗余來解決,或者直接通過創(chuàng)建DTO對象來解決,實際的操作方式和Hibernate原有的操作方式類似,以下我們演示兩種,一種是獲取班級,并且獲得班級中的學生信息,另外一種是獲取學生順便得到學生對象。

通過查詢學生獲取班級信息

這種需求最簡單的的方式是直接創(chuàng)建StudentDto,并且加入Student和Classroom的關聯(lián)。如下所示

public class StudentBadDto {

    private Student stu;
    private Classroom cla;
    public StudentBadDto(Student stu, Classroom cla) {
        super();
        this.stu = stu;
        this.cla = cla;
    }

  //省略了getter和setter方法
}

查詢的代碼如下所示:

@Query("select new org.konghao.model.StudentBadDto(stu,cla) from Student stu,Classroom cla where stu.cid=cla.id")
public List<StudentDto> listBadStu();

以上的處理方式和hibernate類似,通過DTO完成查詢,非常的簡單,測試代碼如下:

@Test
public void testListStu() {
    List<StudentDto> sds = studentRepository.listBadStu();
    Assert.assertEquals(5, sds.size());
    Assert.assertEquals(2, sds.get(0).getStu().getId());
    Assert.assertEquals(1, sds.get(0).getCla().getId());
}

以上方法似乎可以滿足要求,但是我們仔細觀察一下所發(fā)出的sql

springDataJpa的關聯(lián)查詢
springDataJpa的關聯(lián)查詢

我們發(fā)現(xiàn)發(fā)出了很多條sql,它先查詢出了student,然后當需要Classroom的時候又發(fā)出了查詢,而且每個對象都這樣進行了操作,這顯然是我們無法接受的,所以大家記住不能很簡單的用對象來進行處理,我們必須把屬性分開來處理。這其實是hibernate比較建議的一種操作方式,首先dto中不能有任何的對象,只能通過屬性來定義。代碼如下:

public class StudentGoodDto {
    private int sid;
    private String sname;
    private int claId;
    private String claName;
    private String grade;

    public StudentGoodDto() {}

    public StudentGoodDto(int sid,String sname, int claId, String claName, String grade) {
        super();
        this.sid = sid;
        this.sname = sname;
        this.claId = claId;
        this.claName = claName;
        this.grade = grade;
    }

    //省略了getter和setter
}

注意以上構造函數(shù)的寫法,在具體的Query中需要按照這順序來進行構造。

@Query("select new org.konghao.model.StudentGoodDto(stu.id,stu.name,cla.id,cla.name,cla.grade) from Student stu,Classroom cla where stu.cid=cla.id")
public List<StudentGoodDto> listGoodStu();

此時再進行調用之后,我們發(fā)現(xiàn)SQL語句就只剩下一條了

Hibernate: select student0_.id as col_0_0_, student0_.name as col_1_0_, classroom1_.id as col_2_0_, classroom1_.name as col_3_0_, classroom1_.grade as col_4_0_ from t_student student0_ cross join t_classroom classroom1_ where student0_.cid=classroom1_.id

以上就是關聯(lián)對象的查詢方法,但是此處比較麻煩的就是以后如果需要查詢學生對象,都需要寫如此長的Query查詢,這里可以給大家提供一種思路,就是我們自己創(chuàng)建一個properties文件,把這個構造函數(shù)通過字符串存儲起來,每次調用的時候就自動到這個配置文件中去取,但此時就無法使用spring data jpa了,需要創(chuàng)建一個studentRepository來自己實現(xiàn)。這種方式我就不再實現(xiàn)了,掌握之后思路比較的簡單。

最后我們來解決一對多的關系,一對多的關系必須也存儲在dto中。

/**
 * 獲取班級和具體的學生列表
 * @author konghao
 *
 */
publ
public class ClassroomDto {
    private int cid;
    private String name;
    private String grade;
    private List<Student> stus;
}

/**
 * 獲取班級和班級人數(shù)
 * @author konghao
 *
 */
public class ClassroomStuNumDto {
    private int cid;
    private String cname;
    private String grade;
    private long snum;

    public ClassroomStuNumDto(int cid, String cname, String grade, long snum) {
        super();
        this.cid = cid;
        this.cname = cname;
        this.grade = grade;
        this.snum = snum;
    }
}


我們創(chuàng)建了兩個dto來模擬兩種狀態(tài):第一個表示查詢班級時得到班級中的所有學生信息,第二個在查詢班級時獲取班級的學生人數(shù),其實在實際的開發(fā)過程中,第一種情況非常的少見,但是有些時候也會需要,這里會給大家提供一種解決方案。但我們在實際的應用中第二查詢估計會多一些,實現(xiàn)第二種查詢的方式很多,一種是在ClassroomRepositoryCustom中自己來實現(xiàn),,或者直接通過@Query來設定也可以,以下是實現(xiàn)代碼:

@Query("select new org.konghao.model.ClassroomStuNumDto(cla.id,cla.name,cla.grade,count(stu.id)) from Classroom cla,Student stu where cla.id=stu.cid group by cla.id")
public List<ClassroomStuNumDto> listClassrooms();

其實以上方案不是很理想的解決方案,因為使用HQL在沒有對象關聯(lián)的情況下是無法使用left join的,這在做group時無法統(tǒng)計得出學生為0的班級,比較好的解決方案是自己寫這個實現(xiàn),通過sql來處理。

第一種需求的代碼如下:

public interface ClassroomRepositoryCustom {

    @Transactional
    public void delete(int cla);

    /**
     * 查詢班級dto
     * @return
     */
    public List<ClassroomDto> listClassroomDto();
}

首先在ClassroomRepositoryCustom中創(chuàng)建一個查詢班級dto的方法,實現(xiàn)如下:

@Override
public List<ClassroomDto> listClassroomDto() {
    String hql = "select cla.id,cla.name,cla.grade,stu.id,stu.name,stu.age,stu.address from Classroom cla,Student stu where cla.id=stu.cid";
    List<Object[]> list = entityManager.createQuery(hql).getResultList();
    List<ClassroomDto> clas = new ArrayList<ClassroomDto>();
    ClassroomDto cd = null;
    List<Student> stus = null;
    System.out.println(list.size());
    for(Object[] objs:list) {
        Student stu = new Student();
        stu.setId((Integer)objs[3]);
        stu.setName((String)objs[4]);
        stu.setAge((Integer)objs[5]);
        stu.setAddress((String)objs[6]);
        if(!checkExist(clas,(Integer)objs[0])) {
            stus = new ArrayList<Student>();
            stus.add(stu);
            cd = new ClassroomDto();
            cd.setCid((Integer)objs[0]);
            cd.setName((String)objs[1]);
            cd.setGrade((String)objs[2]);
            cd.setStus(stus);
            if(!checkExist(clas, cd.getCid())) clas.add(cd);
        } else {
            cd.getStus().add(stu);
        }
    }
    return clas;
}

private boolean checkExist(List<ClassroomDto> clas, Integer id) {
    for(ClassroomDto cd:clas) {
        if(cd.getCid()==id) return true;
    }
    return false;
}

實現(xiàn)思路就是把所有的數(shù)據(jù)一次性查詢出來然后進行在程序中來進行處理。

到此為止我們Spring Data JPA部分的所有知識都講解完了,我們發(fā)現(xiàn)雖然spring data jpa很好用,但是對于復雜的查詢依然還是得自己寫sql或者hql來解決,所以很多時候大家要根據(jù)項目的需求進行擴展和設計,在這幾講中已經(jīng)把spring data jpa中比較有用的東西都實現(xiàn)了,希望對大家有所幫助,下一塊的知識給大家講解Thymleaf,這是比較好用的一個界面層展示模板。

本文的源代碼在這里:源代碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容