SpringDataJPA——各种类型的方法实现的底层SQL

起因

这个星期都在学习Spring Data JPA,,比如官网上的介绍,各种博客教程,可以知道的就是如何去使用Spring Data JPA。

Spring Data JPA 是对JPA规范的一种包装,神奇的是这个是一个抽象,抽象,抽象,什么意思呢?
就是说:Spring Data JPA 中只有接口和抽象类,不提供实现!!!

所以,如果单独的建立Spring Data JPA的项目,通过外部jar包导入的方式,需要导入的是:

  1. Spring相关的jar包(这个无可厚非,因为Spring Data JPA 是Spring家族的项目)
  2. JPA相关的jar包
  3. Hibernate相关的jar包
  4. Hibernate(JPA)相关的jar包

此次使用的是Hibernate实现。

Spring Data JPA 可以提供查询的几种方式

PS:这里只是择取一部分来看真实的SQL语句是如何的。

  1. 继承接口的方式
    可以继承的接口:
    Repository接口:这是一个空接口,标识接口,标识可以通过JPA规范进行方法命名的查询了。
    CrudRepository接口:这是继承了Repository接口,实现了CRUD(增删改查)操作的接口。
    PagingAndSortingRepository接口:这是继承了CrudRepository接口,并且实现了分页和排序操作的接口。
    JpaRepository接口:这是继承了PagingAndSortingRepository接口,实现对批量操作进行了优化,和一些缓存操作的接口,并且将findXXX类型的方式返回的迭代器进行了进一步封装为List集合。
    以User表为例,user(id,username,password,create_time)
    需要注意的是,不要看着这些SQL语句感觉很头疼,各种什么user0_ 什么user0_.id1_0_0_什么什么的,需要知道的是这些都是别名,别名,别名。其实把这些别名改回去,这些SQL就很简单了!

    这是JPA直接提供的save方法  
    这是传入的User没有id的情况 是添加
    Hibernate: 
        insert 
        into
            t_user
            (create_time, password, username) 
        values
            (?, ?, ?)
    这是传入的User有id的情况 是更新
    Hibernate: 
        update
            t_user 
        set
            create_time=?,
            password=?,
            username=? 
        where
            id=?
    --------------------------------------------------------------------------------------
    这是JPA直接提供的delete方法  首先会去查一遍,然后删
    Hibernate: 
        select
            user0_.id as id1_0_0_,
            user0_.create_time as create_t2_0_0_,
            user0_.password as password3_0_0_,
            user0_.username as username4_0_0_ 
        from
            t_user user0_ 
        where
            user0_.id=?
    Hibernate: 
        delete 
        from
            t_user 
        where
            id=?
    --------------------------------------------------------------------------------
    这是JPA直接提供的count统计方法
    Hibernate: 
        select
            count(*) as col_0_0_ 
        from
            t_user user0_
    --------------------------------------------------------------------------------
    这是JPA直接提供的findAll方法
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_
    --------------------------------------------------------------------------------
    这是JPA直接提供的findAll(Sort)方法
    这是默认的情况,只是 new Sort("id")  默认是升序的
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        order by
            user0_.id asc
    这是new Sort(Sort.Direction.DESC,"id","username")的情况
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        order by
            user0_.id desc,
            user0_.username desc
    --------------------------------------------------------------------------------
    这是JPA直接提供的findAll(Pageable)方法
    	Pageable pageable = new PageRequest(page, size); 
    Pageable是接口,实现类是PageRequest,还有其他的构造方法,这是最简单的
    	Pageable pageable = new PageRequest(1, 2,Direction.DESC,"id");
    使用情况:
    		Pageable pageable = new PageRequest(1, 2);  //page是从0先开始的
    		Page page = userDAO3.findAll(pageable);
    		System.err.println("总记录数:" + page.getTotalElements());
    		System.err.println("总页数:" + page.getTotalPages());
    		System.err.println("数据集:" + page.getContent());
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ limit ?,
            ?
    如果没有产生数据,就会有下列这样的错误:出现错误的数据
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ limit ?,
            ?
    Hibernate: 
        select
            count(user0_.id) as col_0_0_ 
        from
            t_user user0_
    先是查询指定的页面的数据,然后进行了统计
    --------------------------------------------------------------------------------
    这是JPA直接提供的saveAll(Iterator) 多次重复插入
    Hibernate: 
        insert 
        into
            t_user
            (create_time, password, username) 
        values
            (?, ?, ?)
    Hibernate: 
        insert 
        into
            t_user
            (create_time, password, username) 
        values
            (?, ?, ?)
    --------------------------------------------------------------------------------
    这是JPA直接提供的deleteAllInBatch()
    Hibernate: 
        delete
    	from
    		t_user
    --------------------------------------------------------------------------------
    这是JPA直接提供的deleteInBatch(list)  
    这个方法很神奇,我传进去两个对象,但是删除只删除最后一个,但是形参又是迭代器类型,神奇,
    而且最后实现的SQL的条件也是 or
    Hibernate: 
        delete 
        from
            t_user 
        where
            id=? 
            or id=?
    --------------------------------------------------------------------------------
    

    还有一点:对于实现了接口,但是没有具体的通过@Repository注解标注的DAO层接口实现类,通过动态代理的方式,自己定义了的各种XXXDAO接口或者XXXRepository接口,只要继承上诉的这些接口,都会最后在底层有一个动态代理创建的实现类SimpleJpaRepository。
    SimpelJpaRepository实现类,是一个继承了JpaRepository接口和JpaSpecificationExecutor接口(这个接口是用来动态条件查询,进行SQL语句的拼装操作的接口)的一个实现类。
    PS:想知道动态代理怎么代理的,创建的步骤,可以进行断点调试,看运行期调用接口的方法的时候,底层是如何进行实际的调用运行的。我自己观察的好像里面其实是有两个代理类,一个是和Spring AOP相关的动态代理,一个是接口的实现类的一个代理。

  2. 方法查询,通过一定的规范进行的方法命名
    首先需要注意的是一些方法定义的规范(贴张图吧!图片引用来自于:https://www.cnblogs.com/zhaodahai/p/6824275.html)
    SpringDataJPA——各种类型的方法实现的底层SQL_第1张图片
    需要注意的是这个位置的find可以换成get或者read都可以。

    接口中的方法:
    	User getUserById(Integer id);
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.id=?
    --------------------------------------------------------------------------------
    接口中的方法:
    	User findByUsername(String username);
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.username=?
    --------------------------------------------------------------------------------
    接口中的方法:
    	User findByUsernameAndPassword(String username,String password);
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.username=? 
            and user0_.password=?
    --------------------------------------------------------------------------------
    接口中的方法:
    	List findByIdBetween(Integer start,Integer end);
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.id between ? and ?
    --------------------------------------------------------------------------------
    接口中的方法:
    	List findByPasswordIsNotNull();
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.password is not null
    --------------------------------------------------------------------------------
    接口中的方法:
    	List findByUsernameLike(String username);
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.username like ?
    这里需要注意的是 这个位置是没有加入百分号的,想要模糊查询需要我们自己加百分号
    --------------------------------------------------------------------------------
    接口中的方法:
    	List findByUsernameOrderByIdDesc(String username);//降序的方式
    	//List findByUsernameOrderById(String username);//升序的方式
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.username=? 
        order by
            user0_.id desc
    如果是另一种排序 则直接在接口定义的方法中把Desc降序去掉就是Asc升序的排序方式了
    --------------------------------------------------------------------------------
    

    这个方法实现的模糊查询 Like 关键字定义的方法需要注意,这个Like关键字仅仅只是一个关键字,也就是在SQL语句中的条件里加上like关键字,里面是没有 % _ 这些东西的,而是需要自己传进去才行。
    也就是说你以为实现了Like关键字的方法,我传个XX 底层的SQL应该是 like %XX% ,额,很可惜,并不是,底层直接就是 like XX, 这就很尴尬了,这就相当于是精确查询了…

  3. JPQL,通过@Query的实现的方式
    首先需要了解的是@Query支持的语句是以:
    1. select 开头的
    2. update 开头的
    3. delete 开头的
    4. 但是不支持 insert 开头的
    其次需要注意的是:
    update/delete 需要加入另一个注解@Modifying,但是如果你直接通过这个进行单元测试的话,还是会报错,报错:缺少事务管理器异常,也就是说,单元测试的方法上面需要加上注解@Transaction事务注解

    通过JPQL定义的查询:
    	@Query(value = "select u1 from User u1 "
    			+ "where u1.id = (select max(u2.id) from User u2)")
    	User getMaxIdUser();
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.id=(
                select
                    max(user1_.id) 
                from
                    t_user user1_
            )
    --------------------------------------------------------------------------------
    通过JPQL定义的查询:
    	@Query(value = "select u from User u where u.username = ?1 and "
    			+ "password = ?2")
    	User getByUsernameAndPassword(String username,String password);
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.username=? 
            and user0_.password=?
    --------------------------------------------------------------------------------
    通过JPQL定义的查询:
    	@Query(value = "select u from User u where username like %?1%")
    	List getByLikeUsername(String username);
    框架实现的具体的SQL代码:
    Hibernate: 
        select
            user0_.id as id1_0_,
            user0_.create_time as create_t2_0_,
            user0_.password as password3_0_,
            user0_.username as username4_0_ 
        from
            t_user user0_ 
        where
            user0_.username like ?
    --------------------------------------------------------------------------------
    通过JPQL定义的更新/删除: 此处以更新为例  但是没有插入,需要注意
    	@Query(value = "update User u set u.password = ?1 where u.id = ?2")
    	@Modifying
    	void updateUser(String password,Integer id);
    注意这里需要加一个@Modifying注解
    与此同时  如果你执行单元测试
    还需要@Transactional注解
    	@Test
    	@Transactional
    	public void testJPQL() throws Exception {
    框架实现的具体的SQL代码:
    Hibernate: 
        update
            t_user 
        set
            password=? 
        where
            id=?
    --------------------------------------------------------------------------------
    

你可能感兴趣的:(Spring,Data,JPA)