在相当长的一段时间里,实现应用程序的数据访问层非常麻烦。必须编写太多的模板代码来执行简单的查询,以及执行分页。Spring data JPA旨在通过减少实际需要的工作量来显著改进数据访问层的实现。作为开发人员,您可以编写存储库接口,包括自定义查找程序方法。spring data JPA核心接口是Repository,其它所有接口都在此类的基础上进行了扩展。 下面是JPA关系图解图:
CurdRepository提供了增删改产方法,PagingAndSortingRepositroy增加了分页查询方法,JpaRepositroy又增加了实例查询方法。实际应用中可以有选择的继承上面接口即可。上面说过,jpa提供了封装查询数据库模板方法。下面就来一一介绍JPA给我们提供的模板方法:
接口,提供增删改查基本的功能:
@NoRepositoryBean
public interface CrudRepository extends Repository {
S save(S entity);
Iterable save(Iterable entities);
T findOne(ID id);
boolean exists(ID id);
Iterable findAll();
Iterable findAll(Iterable ids);
long count();
void delete(ID id);
void delete(T entity);
void delete(Iterable extends T> entities);
void deleteAll();
}
接口 ,分页查询,和排序:
@NoRepositoryBean
public interface PagingAndSortingRepository extends CrudRepository {
//分类查询
Iterable findAll(Sort sort);
//分页查询
Page findAll(Pageable pageable);
}
接口,提供实例查询方法
@NoRepositoryBean
public interface JpaRepository
extends PagingAndSortingRepository, QueryByExampleExecutor {
S saveAndFlush(S entity);
void deleteInBatch(Iterable entities);
void deleteAllInBatch();
T getOne(ID id);
//实例查询
@Override
List findAll(Example example);
//实例查询,分类排序
@Override
List findAll(Example example, Sort sort);
}
实现类,增加了事务注解,进行事务管理,这就不介绍了,讲到自定义封装基类的时候会用到,一般都不会继承使用这个类.
自定义一个PersonRepository接口继承上述任何一个接口都可以。不需要在接口上加@Repository注解,也不需要写实现类,即可在service,controller层注入使用,是不是很简洁。对于一般的修改保存操作,只要直接调用JPA接口提供的方法就可以用了,这里就不在介绍了。
Person实体类:
@Entity
public class Person {
private Integer id;
private String name;
private String password;
.....省略get set方法
}
PersonRepository 接口
下面介绍了JPA查询方法,条件查询,list集合查询,分页查询,排序查询,sql语句查询,指定字段查询,实例查询,基本上包含了实际开发中所需要的查询方法。
//省略了注释。
public interface PersonRepository extends JpaRepository {
//自定义查询,AND为jpa 关键字,相当于(where x.lastname = ?1 and x.firstname = ?2)
jpa关键字应该都要熟悉
Person findByNameAndPassword(String name ,String password);
//指定字段查询,只返回name
PersonName findById(Integer id);
//排序查询,返回list集合
List findByNameAndPassword(String name ,String password,Sort sort);
//分页查询 查询计算元素总个数总页数,数据多的情况下,代价是昂贵的
Page findByNameAndPassword(String name ,String password,Pageable pageable);
//分页查询,返回的是一个片段,它只知道下一片段或者上一片段是否可用。
Slice findByNameAndPassword(String name,Pageable pageable);
//sql查询。?后面数字,对应方法中参数位置。使用原生sql语句
@Query("select p from person as p where p.name = ?1 and p.password = ?2 ",nativeQuery = true)
Person myfind(String name ,String password);
//实例查询
Person findByExample(Example example)
}
@RestController public class TestController {
@Resource
private PersonRepository personRepository;
@GetMapping(value = "/test")
public String test(Integer personId){
Person p = personRepository.findByNameAndPassword("张三","123");
//排序
List p1 = personRepository.findByNameAndPassword("张三","123",
new Sort(Sort.Direction.ASC,"id"));
//分页
Page p2 = personRepository.findByNameAndPassword("张三",
"123", new PageRequest(1,10,new Sort("id")));
Person tempPerson=new Person();
tempPerson.setId(personId));
//通过实例查询
Person p3= personRepository.findByExample(Example.of(tempPerson))
return p+"+"+ p1 "+"+p2.getTotalElements()+"+"+p3;
}
Keyword | Sample | JPQL snippet |
---|---|---|
And |
findByLastnameAndFirstname |
… where x.lastname = ?1 and x.firstname = ?2 |
Or |
findByLastnameOrFirstname |
… where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals |
findByFirstname,findByFirstnameIs,findByFirstnameEquals |
… where x.firstname = 1? |
Between |
findByStartDateBetween |
… where x.startDate between 1? and ?2 |
LessThan |
findByAgeLessThan |
… where x.age < ?1 |
LessThanEqual |
findByAgeLessThanEqual |
… where x.age <= ?1 |
GreaterThan |
findByAgeGreaterThan |
… where x.age > ?1 |
GreaterThanEqual |
findByAgeGreaterThanEqual |
… where x.age >= ?1 |
After |
findByStartDateAfter |
… where x.startDate > ?1 |
Before |
findByStartDateBefore |
… where x.startDate < ?1 |
IsNull |
findByAgeIsNull |
… where x.age is null |
IsNotNull,NotNull |
findByAge(Is)NotNull |
… where x.age not null |
Like |
findByFirstnameLike |
… where x.firstname like ?1 |
NotLike |
findByFirstnameNotLike |
… where x.firstname not like ?1 |
StartingWith |
findByFirstnameStartingWith |
… where x.firstname like ?1 (parameter bound with appended % ) |
EndingWith |
findByFirstnameEndingWith |
… where x.firstname like ?1 (parameter bound with prepended % ) |
Containing |
findByFirstnameContaining |
… where x.firstname like ?1 (parameter bound wrapped in % ) |
OrderBy |
findByAgeOrderByLastnameDesc |
… where x.age = ?1 order by x.lastname desc |
Not |
findByLastnameNot |
… where x.lastname <> ?1 |
In |
findByAgeIn(Collection |
… where x.age in ?1 |
NotIn |
findByAgeNotIn(Collection |
… where x.age not in ?1 |
True |
findByActiveTrue() |
… where x.active = true |
False |
findByActiveFalse() |
… where x.active = false |
IgnoreCase |
findByFirstnameIgnoreCase |
… where UPPER(x.firstame) |
上面的例子都是一些简单的查询,但实际开发中,我们可能会需要用到复杂的sq、连接查询或者需要查询结果只包含我们需要的字段。如果实体之间的关联关系都配置得当的话,JPA提供的方法也可以满足我们的需求。当然我们也可以自定义一个继承JPA的模板类,然后封装查询的方法:
创建一个BaseRepository ,定义两个sq语句查询:
/**
* repository 基类,封装自定义查询方法
*
* @author
* @date 2017/5/12 8:32
* @Package com.base
* @Version v1.0
*/
@NoRepositoryBean //该注解表示 spring 容器不会创建该对象
public interface BaseRepository extends PagingAndSortingRepository,JpaRepository {
/**
* sql查询
*
* @param sql
* @param args
* @return
*/
List
BaseRepositoryImpl 类,实现BaseRepository 接口:
/**
* @author
* @date 2017/5/12 8:32
* @Package com.base.impl
* @Version v1.0
*/
public class BaseRepositoryImpl extends SimpleJpaRepository implements BaseRepository {
//实体管理类,对持久化实体做增删改查,自动义sq操作模板所需要的核心类
public final EntityManager entityManager;
public BaseRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
@Override
@Transactional(rollbackFor = Exception.class)
public List findAllByParms(String sql, Object... args) {
//获取session
Session session = (Session) entityManager.getDelegate();
org.hibernate.Query q = session.createSQLQuery(sql);
//查询结果转map
q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
int i = 0;
for (Object arg : args
) {
q.setParameter(i++, arg);
}
return q.list();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Page findPageByParams(String sql, Pageable pageable, Object... args) {
Session session = (Session) entityManager.getDelegate();
org.hibernate.Query q = session.createSQLQuery(sql);
q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
int i = 0;
for (Object arg : args
) {
q.setParameter(i++, arg);
}
List totalCount = q.list();
q.setFirstResult(pageable.getPageSize() * (pageable.getPageNumber() - 1));
q.setMaxResults(pageable.getPageSize());
List pageCount = q.list();
Page pages = new PageImpl<>(pageCount, pageable, totalCount.size());
return pages;
}
}
创建一个PersonRepositroy 继承BaseRepository,就可以直接使用定义的查询方法了:
/**
* @author
* @date 2017/5/12 10:07
* @Package com.repository
* @Version v1.0
*/
public interface PersonRepository extends BaseRepository {
}
@RestController public class TestController {
@Resource
private PersonRepository personRepository;
@GetMapping(value = "/test")
public String test( ){
//订单表与用户表关联,通过用户ID查询该用户的所有订单,只获取订单编号和订单详情。
String sql="select o.no,o.detail from person as p inner join order as o on o.personid=p.id and p.id= ? "
Integer userId=11;
Page orderLists = personRepository.findPageByParams(sql,new PageRequest(1,10),userId);
return orderLists;
}
}
@EnableJpaRepositories(basePackages = {"com.repository"}, repositoryBaseClass = BaseRepositoryImpl.class)
public class MybootApplication {
public static void main(String[] args) {
SpringApplication.run(MybootApplication.class, args);
}
}