1. 程式人生 > 實用技巧 >使用Spring Data JPA開發基於JPA的資料訪問層

使用Spring Data JPA開發基於JPA的資料訪問層

Spring Data JPA 環境搭建

參考https://www.cnblogs.com/wumingoo1/p/13414718.html

基本 CRUD

滿足基本 CRUD 的介面

  • 只要讓 DAO 層介面 繼承 JpaRepository,JpaSpecificationExecutor 介面,就自動具有了很多方法,並且不用實現
  • JpaRepository<操作的實體類型別,實體類中主鍵屬性的型別>:封裝了基本CRUD操作
  • JpaSpecificationExecutor<操作的實體類型別>:封裝了複雜查詢(分頁)
  • 注意將 介面寫在 applicationContext.xml 配置檔案中指定的包下
  • CustomerDao.java原始碼如下:

    package cn.wm.dao;
    
    import cn.wm.domain.Customer;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    
    public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
    
    }
    

基本 CRUD 操作測試

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class CRUDTest {
    @Autowired
    private CustomerDao customerDao;

    // 儲存
    @Test
    @Transactional
    @Rollback(false)
    public void test01() {
        Customer c = new Customer();
        c.setCustName("慕課網");
        c.setCustIndustry("IT教育");
        Customer save = customerDao.save(c);
        System.out.println(save == c); // true,且處於 Managed 狀態
        save.setCustName("學堂線上");
    }

    // 更新 【注】:find + setter 也可以實現更新
    @Test
    @Transactional
    @Rollback(false)
    public void test02() {
        Customer c = new Customer();
        c.setCustId(1L);
        c.setCustName("中國大學MOOC");
        Customer save = customerDao.save(c);
        // save 處於 Managed 狀態,c 不處於 Managed 狀態
        save.setCustName("網易雲課堂");
    }

    // 刪除
    @Test
    @Transactional
    @Rollback(false)
    public void test03() {
        customerDao.delete(15L);
    }

    // 立即查詢一個
    @Test
    @Transactional
    @Rollback(false)
    public void test04() {
        Customer c1 = customerDao.findOne(1L);
        System.out.println("---");
        System.out.println(c1);
        /*

        Hibernate: select ...
        ---
        Customer{...}

         */

        Customer c2 = customerDao.findOne(9999L);
        System.out.println("---");
        System.out.println(c2);
        /*

        Hibernate: select ...
        ---
        null

         */
    }

    // 延遲查詢一個
    @Test
    @Transactional
    @Rollback(false)
    public void test05() {
        Customer c1 = customerDao.getOne(1L);
        System.out.println("---");
        System.out.println(c1);
        /*

        ---
        Hibernate: select ...
        Customer{...}

         */


        Customer c2 = customerDao.getOne(9999L);
        System.out.println("---");
        try {
            System.out.println(c2);
        } catch (EntityNotFoundException e) {
            System.out.println("沒查到");
        }
        /*

        ---
        Hibernate: select ...
        沒查到

         */
    }

    // 查詢所有
    @Test
    @Transactional
    @Rollback(false)
    public void test06() {
        List<Customer> list = customerDao.findAll();
        for (Customer c : list) {
            c.setCustLevel("vip");
        }
    }

    // 統計查詢
    @Test
    public void test07() {
        long count = customerDao.count();
        System.out.println("count = " + count);
    }

    // 是否存在
    @Test
    public void test08() {
        boolean exists1 = customerDao.exists(1L);
        System.out.println("id 為 1 的記錄存在:" + exists1);
        boolean exists2 = customerDao.exists(9999L);
        System.out.println("id 為 9999 的記錄存在:" + exists2);
    }
}

多欄位多方式查詢

給介面新增查詢方法

基本 CRUD 裡的方法只能根據 id 欄位精確查詢,要想根據多個欄位多種查詢方式,就需要自定義介面方法。但是隻要這些介面方法的方法名和引數的型別和順序滿足要求,就可以不寫實現直接使用這些方法。

  • 單一欄位精確查詢: findBy + 屬性名(首字母大寫,後面的屬性名首字母也要大寫,省略)
  • 單一欄位模糊查詢: findBy + 屬性名 + Like
  • 單一欄位 IsNull 查詢:findBy + 屬性名 + IsNull
  • 多欄位多方式查詢:findBy + 屬性名 + 查詢方式 + 條件連線符(And | Or)屬性名 + 查詢方式... (引數順序要與屬性順序一致)
  • 底層使用 JPQL 實現
  • 新增的介面方法如下:

    public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
        Customer findByCustName(String custName);
    
        List<Customer> findByCustNameLike(String custName);
    
        List<Customer> findByCustLevelIsNull();
    
        List<Customer> findByCustNameLikeAndCustLevel(String custName, String custLevel);
    }
    

findBy 方法測試

@Test
@Transactional
@Rollback(false)
public void test01() {
    Customer c = customerDao.findByCustName("學堂線上");
    c.setCustLevel("vip");
}

@Test
@Transactional
@Rollback(false)
public void test02() {
    List<Customer> list = customerDao.findByCustNameLike("網易%");
    for (Customer c : list) {
        c.setCustAddress("廣州");
    }
}

@Test
@Transactional
@Rollback(false)
public void test03() {
    List<Customer> list = customerDao.findByCustLevelIsNull();
    for (Customer c : list) {
        c.setCustLevel("normal");
    }
}

@Test
@Transactional
@Rollback(false)
public void test04() {
    List<Customer> list = customerDao.findByCustNameLikeAndCustLevel("網易%", "vip");
    for (Customer c : list) {
        c.setCustLevel(null);
    }
}

JPQL 查詢

給介面新增查詢方法

通過 @Query 註解指定 JPQL 語句可以實現更自由的查詢、更新操作。在 CustomerDao 介面中新增如下方法:

@Query("from Customer where custName like ? and custLevel = ?")
List<Customer> findJpql(String custName, String custLevel);

@Query("update Customer set custName=?2 where custId=?1")
@Modifying
void updateCustomer(Long custId, String custName);

JPQL 方法測試

@Test
@Transactional
@Rollback(false)
public void test01() {
    List<Customer> list = customerDao.findJpql("網易%", "vip");
    for (Customer c : list) {
        c.setCustLevel("svip");
    }
}

@Test
@Transactional
@Rollback(false)
public void test02() {
    customerDao.updateCustomer(1L,"中國大學MOOC");
}

SQL 查詢

給介面新增查詢方法

@Query 註解除了可以指定 JPQL 語句,還可以通過 指定 SQL 語句可以實現查詢操作,只需要讓 @Query 註解的 nativeQuery 屬性設定為 true 即可。在 CustomerDao 介面中新增如下方法:

@Query(value = "select * from cst_customer where cust_name like ? and cust_level = ?", nativeQuery = true)
List<Customer> findSql(String custName, String custLevel);

SQL 方法測試

@Test
@Transactional
@Rollback(false)
public void test01() {
    List<Customer> list = customerDao.findSql("網易%", "svip");
    for (Customer c : list) {
        c.setCustLevel("vip");
    }
}