第三章:使用QueryDSL與SpringDataJPA完成Update&Delete

weixin_33866037發表於2017-07-05

我們上一章講解了有關QueryDsl整合SpringDataJPA完成簡單的單表條件查詢,採用了兩種模式進行查詢一種是完全QueryDsl而另外一種則是整合的形式,既然單表的查詢已經講解接下來我們來看看QueryDsl與SpringDataJPA整合後的Update&Delete的多種處理模式。

本章目標

基於SpringBoot框架平臺完成QueryDsl整合SpringDataJPA單表Update&Delete操作。

構建專案

我們使用idea工具建立一個空的SpringBoot專案,把上一章第二章:使用QueryDSL與SpringDataJPA實現單表普通條件查詢內的配置檔案複製到本章專案中(複製內容包含:application.yml,pom.xml內依賴,Bean,BaseJPA,UserJPA)複製完成後使用maven compile命令完成QueryDsl查詢實體的自動建立,先來完成使用者資訊的更新,下面我們直接進入正題。

更新實體資訊

我們採用兩種方式進行更新實體資訊,一種是完全採用SpringDataJPA的save方法,另外一種則是QueryDsl的update方法,下來我們先來看看SpringDataJPA如何完成更新實體資訊。

使用SpringDataJPA更新實體

SpringDataJPA內建了一個save方法用於儲存、更新實體內容,如果存在主鍵值則更新對應主鍵的row資訊,反則是新增一條新資訊,這一點跟Hibernate的saveOrUpdate方法比較相似。我們先來建立一個UserController控制器,程式碼如下所示:

package com.yuqiyu.querydsl.sample.chapter3.controller;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.yuqiyu.querydsl.sample.chapter3.bean.QUserBean;
import com.yuqiyu.querydsl.sample.chapter3.bean.UserBean;
import com.yuqiyu.querydsl.sample.chapter3.jpa.UserJPA;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/4
 * Time:22:15
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
public class UserController
{
    @Autowired
    private UserJPA userJPA;

    //實體管理者
    @Autowired
    private EntityManager entityManager;

    //JPA查詢工廠
    private JPAQueryFactory queryFactory;

    @PostConstruct
    public void initFactory()
    {
        queryFactory = new JPAQueryFactory(entityManager);
        System.out.println("init JPAQueryFactory successfully");
    }
}

上面這個控制器與第二章內的控制器基本內容沒有什麼差別,在bean建構函式初始化時通過EntityManager物件例項化JPAQueryFactory查詢工廠實體,方便我們接下來的查詢操作,QueryDsl形式是需要建立在JPAQueryFactory物件基礎上構建的。SpringDataJPA方式更新實體程式碼如下:

    /**
     * 使用Jpa更新會員資訊
     * @param userBean
     */
    @RequestMapping(value = "/updateWithJpa")
    public String updateWithJpa(UserBean userBean)
    {
        //儲存會員資訊相當於Hibernate內的SaveOrUpdate
        userJPA.save(userBean);
        return "SUCCESS";
    }

很簡單並沒有什麼有難理解的地方,引數是通過介面Request方式傳遞的,下面我們執行下專案在上一章資料基礎上嘗試下更新操作。執行專案控制檯輸出下圖1所示:

4461954-e948ae2a916d144c.png
圖1

看到上圖1中紅色標出部分我們就可以通過瀏覽器進行訪問路徑嘗試更新實體資訊了,下面我們來訪問下地址:127.0.0.1:8080/updateWithJpa?id=6&name=change&age=23&address=SdJN&pwd=123456,介面輸出內容如下圖2所示:
4461954-7590b995f33a3d79.png
圖2

我們來看下控制檯輸出的日誌內容,如下程式碼所示:

Hibernate: 
    select
        userbean0_.t_id as t_id1_0_0_,
        userbean0_.t_address as t_addres2_0_0_,
        userbean0_.t_age as t_age3_0_0_,
        userbean0_.t_name as t_name4_0_0_,
        userbean0_.t_pwd as t_pwd5_0_0_ 
    from
        t_user userbean0_ 
    where
        userbean0_.t_id=?
Hibernate: 
    update
        t_user 
    set
        t_address=?,
        t_age=?,
        t_name=?,
        t_pwd=? 
    where
        t_id=?

可以看到SpringDataJPA先去資料庫查詢了一邊當前物件,比對發現與資料庫內不一致並且存在主鍵值則執行了下面的Update語句,這裡如果查詢到的欄位對應更新的內容一致時則不會執行下面的Update語句,我們再來訪問下剛才的地址檢視控制檯輸出如下程式碼塊所示:

Hibernate: 
    select
        userbean0_.t_id as t_id1_0_0_,
        userbean0_.t_address as t_addres2_0_0_,
        userbean0_.t_age as t_age3_0_0_,
        userbean0_.t_name as t_name4_0_0_,
        userbean0_.t_pwd as t_pwd5_0_0_ 
    from
        t_user userbean0_ 
    where
        userbean0_.t_id=?

可以看到僅僅執行了查詢並沒有發起更新操作。

使用QueryDsl更新實體

下面我們來完全使用QueryDsl來更新實體,程式碼如下所示:

/**
     * 使用QueryDsl更新會員資訊
     * @param userBean
     */
    @RequestMapping(value = "/updateWithQueryDsl")
    public String updateWithQueryDsl(UserBean userBean)
    {
        //querydsl查詢實體
        QUserBean _Q_user = QUserBean.userBean;

        queryFactory
                .update(_Q_user)//更新物件
                //更新欄位列表
                .set(_Q_user.name,userBean.getName())
                .set(_Q_user.address,userBean.getAddress())
                .set(_Q_user.age,userBean.getAge())
                .set(_Q_user.pwd,userBean.getPwd())
                //更新條件
                .where(_Q_user.id.eq(userBean.getId()))
                //執行更新
                .execute();
        return "SUCCESS";
    }

我們第一步先獲取了QUserBean查詢物件,並且通過JPAQueryFactory物件構建了update方法處理,而update的引數就是需要更新的查詢實體,當然update方法內僅支援更新單個查詢實體。

接下來我們就設定要更新的欄位內容了,這裡就是我們隨心所欲控制了。需要更新哪些欄位就設定對應欄位更新的內容即可。

設定完成更新欄位後需要設定更新的條件,不設定也是可以的,當然這裡肯定跟原生SQL一樣,不設定條件就更新表內全部的資料。

最後一步至關重要,如果不呼叫execute方法就不會執行更新操作。下面重啟專案後訪問地址:127.0.0.1:8080/updateWithQueryDsl?id=6&name=changeWithQueryDsl&age=24&address=山東濟南&pwd=666666,介面輸出內容如下圖4所示:

4461954-3ed6067bca583a49.png
圖4
,我們發現這裡出現了系統異常,我們來看下控制檯輸出的錯誤資訊如下:

javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:54) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
    at com.querydsl.jpa.impl.JPAUpdateClause.execute(JPAUpdateClause.java:77) ~[querydsl-jpa-4.1.4.jar:na]
    at com.yuqiyu.querydsl.sample.chapter3.controller.UserController.updateWithQueryDsl(UserController.java:76) ~[classes/:na]
.......

發現如果想執行update/delete方法時必須存在一個事務才可以,那我們修改更新方法新增事務註解@Transactional,重啟專案再來訪問我們之前的地址,介面輸出內容如下圖5所示:


4461954-08ec7d76c93dac33.png
圖5

,介面輸出了執行成功的提示,我們來看下控制檯輸出的SQL語句:

Hibernate: 
    update
        t_user 
    set
        t_name=?,
        t_address=?,
        t_age=?,
        t_pwd=? 
    where
        t_id=?

控制檯輸出的這個SQL語句就是QueryDsl根據我們配置的更新實體、更新欄位、查詢條件自動生成的,是不是比較靈活?

使用SpringDataJPA刪除實體資訊

下面我們來看看SpringDataJPA刪除實體資訊時該怎麼處理?程式碼如下所示:

    /**
     * 使用Jpa刪除會員資訊
     * @param userBean
     */
    @RequestMapping(value = "/deleteWithJpa")
    public String deleteWithJpa(UserBean userBean)
    {
        //執行刪除指定主鍵的值
        userJPA.delete(userBean.getId());
        return "SUCCESS";
    }

上面程式碼我們根據主鍵刪除了指定的實體,下面我們重啟專案後訪問地址:http://127.0.0.1:8080/deleteWithJpa?id=6,控制檯輸出的SQL內容如下所示:

Hibernate: 
    delete 
    from
        t_user 
    where
        t_id=?

輸出的SQL也是按照我們指定的主鍵完成了刪除邏輯。

使用QueryDsl刪除會員資訊

在編寫刪除方法之前我們想到了之前使用QueryDsl更新實體時需要新增事務,當然在刪除的時候也是需要的所以我們編寫刪除方法時要注意,刪除程式碼如下所示:

 /**
     * 使用QueryDsl刪除會員資訊
     * @param userBean
     */
    @RequestMapping(value = "/deleteWithQueryDsl")
    @Transactional
    public String deleteWithQueryDsl(UserBean userBean)
    {
        //querydsl查詢實體
        QUserBean _Q_user = QUserBean.userBean;

        queryFactory
                //刪除物件
                .delete(_Q_user)
                //刪除條件
                .where(_Q_user.id.eq(userBean.getId()))
                //執行刪除
                .execute();
        return "SUCCESS";
    }

QueryDsl的刪除編寫與更新幾乎是差不多的,只是把update方法改成了delete方法,當然where條件也可以新增多個,這個需要看個人實際業務邏輯。

刪除語句編寫完成後呼叫execute方法後QueryDsl才去處理刪除邏輯。重啟專案後,訪問地址:127.0.0.1:8080/deleteWithQueryDsl?id=7,檢視控制檯輸出SQL內容如下所示:

Hibernate: 
    delete 
    from
        t_user 
    where
        t_id=?

下面我們修改下刪除條件,我們根據名稱以及年齡大於20歲條件執行刪除,修改後的方法程式碼如下所示:

    @RequestMapping(value = "/deleteWithQueryDsl")
    @Transactional
    public String deleteWithQueryDsl(UserBean userBean)
    {
        //querydsl查詢實體
        QUserBean _Q_user = QUserBean.userBean;

        queryFactory
                //刪除物件
                .delete(_Q_user)
                //刪除條件
                .where(
                        _Q_user.name.eq(userBean.getName())
                        .and(_Q_user.age.gt(20))
                )
                //執行刪除
                .execute();
        return "SUCCESS";
    }

重啟專案後訪問地址:http://127.0.0.1:8080/deleteWithQueryDsl?name=admin&age=20,控制檯輸出SQL如下所示:

Hibernate: 
    delete 
    from
        t_user 
    where
        t_name=? 
        and t_age>?

輸出的SQL完全根據我們設定的條件來自動生成,QueryDsl內的條件可以跟原生SQL完全一樣,可以完全採用SQL的思想來編寫條件。

總結

以上內容就是本章的全部內容,本章主要講解了QueryDsl是怎麼操作單表Delete&Update操作以及SpringDataJPA操作單表Delete&Update。
本章程式碼已經上傳到碼雲:
SpringBoot配套原始碼地址:https://gitee.com/hengboy/spring-boot-chapter
SpringCloud配套原始碼地址:https://gitee.com/hengboy/spring-cloud-chapter
SpringBoot相關係列文章請訪問:目錄:SpringBoot學習目錄
QueryDSL相關係列文章請訪問:QueryDSL通用查詢框架學習目錄
SpringDataJPA相關係列文章請訪問:目錄:SpringDataJPA學習目錄
感謝閱讀!

加入知識星球,恆宇少年帶你走以後的技術道路!!!

4461954-a56b71bb85d168f9.JPG
微信掃碼加入

相關文章