使用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");
    }
}

Specification 动态查询

Specification 动态查询简介

        JpaSpecificationExecutor 接口具有以下方法,这些方法会根据传入实参的不同生成不同的 SQL,并且都含有 Specification 参数,因而称使用它们的查询为 Specification 动态查询。CutomerDao 继承了 JpaSpecificationExecutor,这些方法不用自己实现,可以直接使用。

public interface JpaSpecificationExecutor<T> {

    T findOne(Specification<T> spec);

    List<T> findAll(Specification<T> spec);

    Page<T> findAll(Specification<T> spec, Pageable pageable);

    List<T> findAll(Specification<T> spec, Sort sort);

    long count(Specification<T> spec);
}

Specification 动态查询测试

// 查询 客户名称为网易公开课 的客户
@Test
@Transactional
@Rollback(false)
public void test01() {
    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //1. 获取比较的属性
            Path<Object> custName = root.get("custName");
            //2. 构造查询条件
            Predicate p = cb.equal(custName, "网易公开课");
            return p;
        }
    };
    Customer c = customerDao.findOne(spec);
    System.out.println(c);
    c.setCustAddress("广州");
}

// 查询 客户名称为网易公开课 且 行业为教育 的客户
@Test
@Transactional
@Rollback(false)
public void test02() {
    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //1. 构造 指定客户名为网易公开课 的查询条件
            Path<Object> custName = root.get("custName");
            Predicate p1 = cb.equal(custName, "网易公开课");
            //2. 构造 指定行业为教育 的查询条件
            Path<Object> custIndustry = root.get("custIndustry");
            Predicate p2 = cb.equal(custIndustry, "教育");
            //3. 构造 指定客户名为网易公开课 且 指定行业为教育 的查询条件
            Predicate and = cb.and(p1, p2);
            return and;
        }
    };

    Customer c = customerDao.findOne(spec);
    System.out.println(c);
    c.setCustLevel("vip");
}

// 模糊查询 客户名称以网易开头 的客户
@Test
@Transactional
@Rollback(false)
public void test03() {
    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //1. 获取比较的属性
            Path<Object> custName = root.get("custName");
            //2. 构造查询条件
            Predicate like = cb.like(custName.as(String.class), "网易%");
            return like;
        }
    };

    List<Customer> list = customerDao.findAll(spec);
    for (Customer c : list) {
        System.out.println(c);
        c.setCustLevel(null);
    }
}

// 添加排序功能
@Test
@Transactional
@Rollback(false)
public void test04() {
    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //1. 获取比较的属性
            Path<Object> custName = root.get("custName");
            //2. 构造查询条件
            Predicate like = cb.like(custName.as(String.class), "网易%");
            return like;
        }
    };
    Sort sort = new Sort(Sort.Direction.DESC, "custId");
    /**
     * Sort(Sort.Direction.DESC, "custId")
     *      第一个参数:排序方向
     *      第二个参数:排序属性
     */
    List<Customer> list = customerDao.findAll(spec, sort);
    for (Customer c : list) {
        System.out.println(c);
        c.setCustLevel("normal");
    }
}

// 添加分页功能
@Test
@Transactional
@Rollback(false)
public void test05() {
    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //1. 获取比较的属性
            Path<Object> custName = root.get("custName");
            //2. 构造查询条件
            Predicate like = cb.like(custName.as(String.class), "网易%");
            return like;
        }
    };
    Pageable pageable = new PageRequest(0,2);
    /**
     * PageRequest(int page, int size)
     *      第一个参数:当前查询的页数(从0开始)
     *      第二个参数:每页查询的数量
     */
    Page<Customer> page = customerDao.findAll(spec, pageable);
    /**
     * Page 是 Spring Data 提供好的 pageBean
     *      page.getContent() 数据列表
     *      page.getTotalElements() 总记录数
     *      page.getTotalPages() 总页数
     */
    System.out.println(page.getContent());
    System.out.println(page.getTotalElements());
    System.out.println(page.getTotalPages());
    for (Customer c : page.getContent()) {
        c.setCustLevel("ssvip");
    }
}

// 模糊查询 客户名称以网易开头 的客户的记录数
@Test
public void test06() {
    Specification<Customer> spec = new Specification<Customer>() {
        @Override
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //1. 获取比较的属性
            Path<Object> custName = root.get("custName");
            //2. 构造查询条件
            Predicate like = cb.like(custName.as(String.class), "网易%");
            return like;
        }
    };
    long count = customerDao.count(spec);
    System.out.println(count);
}
posted @ 2020-08-01 17:59  学习java进行时  阅读(271)  评论(0编辑  收藏  举报