上篇文章介绍了 Repository接口的使用(Spring Data JPA介绍与Spring的整合),接下来重点掌握 Repository的CrudRepository子接口下的子接口。
在dao层的接口中定义方法,查询的方法:find或get或read开头,遵守一些规定:
Keyword | Sample | JPQL snippet |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Repository的子接口的使用时只需要 dao层继承该接口即可。
CrudRepository接口还有一个PagingAndSortingRepository子接口:
CrudRepository 主要提供CRUD功能。
PagingAndSortingRepository 提供进行分页和排序记录的方法。
1、CrudRepository 主要提供CRUD功能。
CrudRepository接口的方法:很好理解就不解释了,下面使用几个即可快速掌握。
public interface UserDao extends CrudRepository {
}
测试:
@Test
public void test() {
User user = new User();
user.setUsername("赵云");
user.setPassword("123456");
user.setAge(18);
user.setCreatetime(LocalDateTime.now());
User save = userDao.save(user);
System.out.println(save.getId()); // 4
}
@Test
public void testList() {
List list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
User user = new User();
user.setUsername("赵云" + i);
user.setPassword("123456");
user.setAge(18 + i);
user.setCreatetime(LocalDateTime.now());
list.add(user);
}
List saveAll = (List) userDao.saveAll(list);
// id 不存在,会报错:org.springframework.dao.EmptyResultDataAccessException:
// No class cn.jq.springdatajpademo.model.User entity with id 2 exists!
userDao.deleteById(2L);
}
2、PagingAndSortingRepository 提供进行分页和排序记录的方法。
Pageable是一个接口,这里使用 PageRequest类
测试:
@Test
public void test() {
Sort.Order order1 = new Sort.Order(Sort.Direction.ASC, "age");
Sort.Order order2 = new Sort.Order(Sort.Direction.DESC, "id");
Sort sort = Sort.by(order1, order2);
// 查询第3页
Pageable pageRequest = PageRequest.of(2, 10, sort);
Page page = userDao.findAll(pageRequest);
List userList = page.getContent(); // 数据
int number = page.getNumber(); // 当前页码,注意默认从0开始
int totalPages = page.getTotalPages(); // 总页码数
int numberOfElements = page.getNumberOfElements(); // 当前页码记录数
long totalElements = page.getTotalElements(); // 总的数量
int size = page.getSize(); // 每页的记录数
System.out.println("userList = " + userList);
System.out.println("number = " + number + 1);
System.out.println("totalPages = " + totalPages);
System.out.println("numberOfElements = " + numberOfElements);
System.out.println("totalElements = " + totalElements);
System.out.println("size = " + size);
}
3、JpaRepository接口是项目开发时使用最多的接口。
JpaRepository继承自PagingAndSortingRepository接口,JpaRepository基于JPA的Repository接口,极大减少了JPA作为数据访问的代码,JpaRepository是实现Spring Data JPA技术访问数据库的关键接口。
public interface UserDao extends JpaRepository {
}
测试:
@Test
public void test() {
User user = new User();
user.setUsername("赵云胜多负少");
user.setPassword("123456");
user.setAge(18);
user.setCreatetime(LocalDateTime.now());
// 主键存在update,不存在就insert操作
// user.setId(4L);
User saveAndFlush = userDao.saveAndFlush(user);
System.out.println(saveAndFlush.getId());
}
JpaSpecificationExecutor 是 JPA 2.0 提供的 Criteria API 的使用封装,可以用于动态生成 Query 来满足我们业务中的各种复杂场景。
Spring Data JPA 为我们提供了 JpaSpecificationExecutor 接口。该接口通过Specification来定义查询条件,使用Specification的要点就是CriteriaBuilder,通过这个对象来创建条件,之后返回一个Predicate对象。这个对象中就有了相应的查询需求,我们同样可以定义多个Specification,之后通过Specifications对象将其连接起来。
通过源码看出:SimpleJpaRepository类是JpaSpecificationExecutor接口的默认实现。
测试:
public interface UserDao extends JpaRepository, JpaSpecificationExecutor {
}
@Test
public void test() {
Specification s1 = new Specification() {
/**
* @param *root: 代表查询的实体类.
* @param criteriaQuery: 可以从中可到 Root 对象, 即告知 JPA Criteria 查询要查询哪一个实体类. 还可以
* 来添加查询条件, 还可以结合 EntityManager 对象得到最终查询的 TypedQuery 对象.使用不多
* @param *criteriaBuilder: CriteriaBuilder 对象. 用于创建 Criteria 相关对象的工厂. 当然可以从中获取到 Predicate 对象
* @return: *Predicate 类型, 代表一个查询条件.
*/
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path age = root.get("age");
Predicate p1 = criteriaBuilder.greaterThan(age, 18);
Path username = root.get("username");
Predicate p2 = criteriaBuilder.like(username, "%李四%");
// where user0_.age>18 and ( user0_.username like ? )
return criteriaBuilder.and(p1, p2);
}
};
List list1 = userDao.findAll(s1);
System.out.println(list1);
}
如果SpringData提供的JPA接口没有我们需要的查询方式,也是可以自定义Repository接口的。
spring data除了已经给我们提供了大量的 Repository接口,它支持我们自己定义Repository接口来扩展功能,实现一些个性化的需求。
1、自定义Repository接口的具体步骤:
1)新建一个自定义接口
// 自定义 Repository接口
public interface MyUserRepository {
List findByAge(Integer age);
}
2)新建该自定义接口的实现类,并对方法进行实现,需要注入 EntityManager对象完成对数据库的操作
public class MyUserRepositoryImpl implements MyUserRepository {
// 这里使用JPA技术实现
@PersistenceContext
private EntityManager entityManager;
@Override
public List findByAge(Integer age) {
String jpql = "select u from User u where u.age <= ?1";
Query query = entityManager.createQuery(jpql, User.class);
query.setParameter(1, age);
List resultList = query.getResultList();
return resultList;
}
}
注意:实现类的名称必须是该自定义接口的名称+Impl,名称会由SpringData解析。
EntityManager是JPA中用于增删改查的接口,它的作用相当于一座桥梁,连接内存中的 Java对象和数据库的数据存储。也可以根据它进行sql的原生查找。
3)在目标接口(dao层接口)中继承该自定义接口。
public interface UserDao extends JpaRepository, MyUserRepository {
// spring data jpa 方法
List findUsersByUsernameLikeAndAgeLessThanEqual(String username, Integer age);
}
4)测试类,目标接口(dao层接口)就可以使用自定义接口方法
@Test
public void testList() {
List userList = userDao.findByAge(18);
System.out.println(userList);
List userList2 = userDao.findUsersByUsernameLikeAndAgeLessThanEqual("%赵云%", 18);
System.out.println(userList2);
}
参考文章:
Spring Data JPA之JpaSpecificationExecutor复杂动态查询实例
—— Stay Hungry. Stay Foolish. 求知若饥,虚心若愚。