这个星期都在学习Spring Data JPA,,比如官网上的介绍,各种博客教程,可以知道的就是如何去使用Spring Data JPA。
所以,如果单独的建立Spring Data JPA的项目,通过外部jar包导入的方式,需要导入的是:
此次使用的是Hibernate实现。
PS:这里只是择取一部分来看真实的SQL语句是如何的。
继承接口的方式
可以继承的接口:
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相关的动态代理,一个是接口的实现类的一个代理。
方法查询,通过一定的规范进行的方法命名
首先需要注意的是一些方法定义的规范(贴张图吧!图片引用来自于:https://www.cnblogs.com/zhaodahai/p/6824275.html)
需要注意的是这个位置的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, 这就很尴尬了,这就相当于是精确查询了…
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=?
--------------------------------------------------------------------------------