一、jpa、spring-data-jpa、mybatis、hibernate认识?
jpa:Java Persistence API、java持久层api(简言之:全是接口规范)
spring-data-jpa:spring家族实现jpa规范的框架称为:spring-data-jpa,易处理类似于单表CRUD,较为依赖实体
mybatis:半自动ORM框架,灵活度较高,易处理复杂sql
hibernate:全自动ORM框架、上手较mybatis难,易处理类似于单表CRUD,较为依赖实体
mybatis-plus:类似于spring-data-jpa提供了一系列CRUD接口,较为依赖实体
二、spring-data-jpa主要接口
CrudRepository接口(CRUD操作):
//CrudRepository接口使用
public interface UserRepository extends CrudRepository<User, Integer> {}
//CrudRepository接口源码
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S entity);//保存
<S extends T> Iterable<S> save(Iterable<S> entities);//批量保存
T findOne(ID id);//根据id查询一个对象
boolean exists(ID id);//判断对象是否存在
Iterable<T> findAll();//查询所有的对象
Iterable<T> findAll(Iterable<ID> ids);//根据id列表查询所有的对象
long count();//计算对象的总个数
void delete(ID id);//根据id删除
void delete(T entity);//删除对象
void delete(Iterable<? extends T> entities);//批量删除
void deleteAll();//删除所有
}
PagingAndSortingRepository接口(分类、排序)继承了CrudRepository接口
//PagingAndSortingRepository接口使用
public interface UserRepositoryWithOrder extends PagingAndSortingRepository<User,Integer> {}
//PagingAndSortingRepository接口源码
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);// 不带分页的排序
Page<T> findAll(Pageable pageable);// 带分页的排序
}
JpaRepository接口
如果业务需要即提供CRUD操作,又需要提供分页以及排序功能,那么就可以直接继承这个接口。
该接口继承了PagingAndSortingRepository接口
//JpaRepository接口源码
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
List<T> findAll();//查询所有对象,不排序
List<T> findAll(Sort sort);//查询所有对象,并排序
<S extends T> List<S> save(Iterable<S> entities);//批量保存
void flush();//强制缓存与数据库同步
T saveAndFlush(T entity);//保存并强制同步
void deleteInBatch(Iterable<T> entities);//批量删除
void deleteAllInBatch();//删除所有
}
JpaSpecificationExecutor接口相对特殊,一般用于相对复杂查询
三、上面的满足一些基本CRUD需求、自定义查询方式?
1.@Query 创建查询(类似于mybatis的@Mapper注解功能,个人倾向于使用@Mapper注解)
/**
* 描述:自定义查询,当Spring Data JPA无法提供时,需要自定义接口,此时可以使用这种方式
*/
public interface UserDefineBySelf extends JpaRepository<User, Integer> {
/**
* 命名参数
* 描述:推荐使用这种方法,可以不用管参数的位置
*/
@Query("select u from User u where u.name = :name")
User findUserByName(@Param("name") String name);
/**
* 索引参数
* 描述:使用?占位符
*/
@Query("select u from User u where u.email = ?1")// 1表示第一个参数
User findUserByEmail(String email);
/**
* 描述:可以通过@Modifying和@Query来实现更新
* 注意:Modifying queries的返回值只能为void或者是int/Integer
*/
@Modifying
@Query("update User u set u.name = :name where u.id = :id")
int updateUserById(@Param("name") String name, @Param("id") int id);
}
2.使用@NamedQueries创建查询
3.通过解析方法名创建查询
public interface SimpleConditionQueryRepository extends JpaRepository<User, Integer> {
/**
* 说明:按照Spring data 定义的规则,查询方法以find|read|get开头
* 涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写
*/
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name = :name and u.email = :email
* 参数名大写,条件名首字母大写,并且接口名中参数出现的顺序必须和参数列表中的参数顺序一致
*/
User findByNameAndEmail(String name, String email);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name = ?1 or u.password = ?2
*/
List<User> findByNameOrPassword(String name, String password);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id between ?1 and ?2
*/
List<User> findByIdBetween(Integer start, Integer end);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id < ?1
*/
List<User> findByIdLessThan(Integer end);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id > ?1
*/
List<User> findByIdGreaterThan(Integer start);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name is null
*/
List<User> findByNameIsNull();
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name is not null
*/
List<User> findByNameIsNotNull();
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name like ?1
*/
List<User> findByNameLike(String name);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name not like ?1
*/
List<User> findByNameNotLike(String name);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.password = ?1 order by u.id desc
*/
List<User> findByPasswordOrderByIdDesc(String password);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name <> ?1
*/
List<User> findByNameNot(String name);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id in ?1
*/
List<User> findByIdIn(List<Integer> ids);
/**
* 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id not in ?1
*/
List<User> findByIdNotIn(List<Integer> ids);
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class SimpleConditionQueryRepositoryTest {
@Autowired
private SimpleConditionQueryRepository dao;
/**
* select
user0_.id as id0_,
user0_.account as account0_,
user0_.email as email0_,
user0_.name as name0_,
user0_.password as password0_
from
USER user0_
where
user0_.name=?
and user0_.email=? limit ?
*/
@Test
public void testFindUserByNameAndEmail(){
User user = dao.findByNameAndEmail("chhliu", "chhliu@.com");
System.out.println(JSON.toJSONString(user));
}
/**
* select
user0_.id as id1_,
user0_.account as account1_,
user0_.email as email1_,
user0_.name as name1_,
user0_.password as password1_
from
USER user0_
where
user0_.name=?
or user0_.password=?
*/
@Test
public void testFindUserByNameOrPassword(){
List<User> users = dao.findByNameOrPassword("chhliu", "123456");
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id1_,
user0_.account as account1_,
user0_.email as email1_,
user0_.name as name1_,
user0_.password as password1_
from
USER user0_
where
user0_.id between ? and ?
*/
@Test
public void testFindByIdBetween(){
List<User> users = dao.findByIdBetween(5, 8);
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id1_,
user0_.account as account1_,
user0_.email as email1_,
user0_.name as name1_,
user0_.password as password1_
from
USER user0_
where
user0_.id<?
*/
@Test
public void testFindByIdLessThan(){
List<User> users = dao.findByIdLessThan(4);
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id0_,
user0_.account as account0_,
user0_.email as email0_,
user0_.name as name0_,
user0_.password as password0_
from
USER user0_
where
user0_.id>?
*/
@Test
public void testFindByIdGreaterThan(){
List<User> users = dao.findByIdGreaterThan(6);
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id0_,
user0_.account as account0_,
user0_.email as email0_,
user0_.name as name0_,
user0_.password as password0_
from
USER user0_
where
user0_.name is null
*/
@Test
public void testFindByNameIsNull(){
List<User> users = dao.findByNameIsNull();
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id1_,
user0_.account as account1_,
user0_.email as email1_,
user0_.name as name1_,
user0_.password as password1_
from
USER user0_
where
user0_.name is not null
*/
@Test
public void testFindByNameIsNotNull(){
List<User> users = dao.findByNameIsNotNull();
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id1_,
user0_.account as account1_,
user0_.email as email1_,
user0_.name as name1_,
user0_.password as password1_
from
USER user0_
where
user0_.name like ?
*/
@Test
public void testFindByNameLike(){
List<User> users = dao.findByNameLike("chhliu");
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id0_,
user0_.account as account0_,
user0_.email as email0_,
user0_.name as name0_,
user0_.password as password0_
from
USER user0_
where
user0_.name not like ?
*/
@Test
public void testFindByNameNotLike(){
List<User> users = dao.findByNameNotLike("chhliu");
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id0_,
user0_.account as account0_,
user0_.email as email0_,
user0_.name as name0_,
user0_.password as password0_
from
USER user0_
where
user0_.password=?
order by
user0_.id desc
*/
@Test
public void testFindByPasswordOrderByIdDesc(){
List<User> users = dao.findByPasswordOrderByIdDesc("123456");
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id1_,
user0_.account as account1_,
user0_.email as email1_,
user0_.name as name1_,
user0_.password as password1_
from
USER user0_
where
user0_.name<>?
*/
@Test
public void testFindByNameNot(){
List<User> users = dao.findByNameNot("chhliu");
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id1_,
user0_.account as account1_,
user0_.email as email1_,
user0_.name as name1_,
user0_.password as password1_
from
USER user0_
where
user0_.id in (
? , ? , ? , ?
)
*/
@Test
public void testFindByIdIn(){
List<User> users = dao.findByIdIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));
System.out.println(JSON.toJSONString(users));
}
/**
* select
user0_.id as id0_,
user0_.account as account0_,
user0_.email as email0_,
user0_.name as name0_,
user0_.password as password0_
from
USER user0_
where
user0_.id not in (
? , ? , ? , ?
)
*/
@Test
public void testFindByIdNotIn(){
List<User> users = dao.findByIdNotIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));
System.out.println(JSON.toJSONString(users));
}
}
解析规则:
And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd)
Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr)
Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min)
LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max)
GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min)
IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull()
IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull()
NotNull --- 与 IsNotNull 等价
Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user)
NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user)
OrderBy ---等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user)
Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user)
In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数
NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数
四、创建查询的顺序
上述三种查询顺序的优先级是啥呢?(布吉岛有默认没,默认是啥咱也不清楚,咱也不敢问)
Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述情况可用,它该优先采用哪种策略呢?为此, 提供了 query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值:
create — 通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 @Query 指定的查询语句,都将会被忽略。
create-if-not-found — 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方法名字来创建查询。这是 query-lookup-strategy 属性的默认值。
use-declared-query — 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常。