spring data jpa 分页怎么查询自定义函数

开始使用 Spring Data JPA - 技术翻译 - 开源中国社区
当前访客身份:游客 [
已有文章 2322 篇
当前位置:
开始使用 Spring Data JPA
英文原文:
0人收藏此文章,
推荐于 3年前 (共 9 段, 翻译完成于 09-18) ()
参与翻译(5人):
在我们刚刚发布项目Spring Data JPA的第一个里程碑时,我想给你一个关于它的简要介绍.正如你所知道的,Spring framework 对于基于JPA的数据存取层提供了支持.那么 Spring Data JPA 是如何添加到Spring中的呢?回答这个问题,我想从一个数据存取组件开始.这个组件提供了一个简单的域(domain),它是用纯JPA和Spring实现的,而且可以扩展和改进.在我们实现之后,我将用Spring Data JPA 来重构它. 你在以在
上找到这个小项目的每一次重构的详细指导.
&翻译的不错哦!
域(The domain)
为了保持简单,我从最简单常用的域开始:客户(Customer)和帐号(Account)
public class Customer {
@GeneratedValue(strategy = GenerationType.AUTO)
// … methods omitted
public class Account {
@GeneratedValue(strategy = GenerationType.AUTO)
@ManyToOne
@Temporal(TemporalType.DATE)
private Date expiryD
// … methods omitted
帐户只有一个到期日(expriyDate).再无其他. - 它使用JPA注解.现在我们来看看管理帐号的组件.
@Repository
@Transactional(readOnly = true)
class AccountServiceImpl implements AccountService {
@PersistenceContext
private EntityM
@Transactional
public Account save(Account account) {
if (account.getId() == null) {
em.persist(account);
return em.merge(account);
public List&Account& findByCustomer(Customer customer) {
TypedQuery query = em.createQuery("select a from Account a where a.customer = ?1", Account.class);
query.setParameter(1, customer);
return query.getResultList();
为了在后面重构引入存储层(repository layer)时不引起名称冲突,我特意命名为 class*Service.但是在概念上,这个类是一个存储对象,而不是服务.事实上我们有吗?
&翻译的不错哦!
这个被@Repository 注释的类抛出的异常可以被Spring的DataAccessException捕获。另外我们还用到了@Transactional 来保证 save(...) 操作的事务性和该类其他方法(这里是findByCustomer(...))的事务只读标识。这样会对数据库性能和我们持久化提供器的性能有一定的优化。
由于我们不想让程序员去决定什么时候调用EntityManager的merge(...)或者persist(...)方法。我们用了实体类主键字段内容来判断到底调用哪一个。这是判断逻辑是全局通用的所以不用对每一个域对象都去声明实现。查询方法也是很直观的,我们写一个查询,绑定一个参数然后执行这条查询并取得结果。这个方法名其实已经很直观了,其实我们可以根据方法名就推出查询语句,所以这里是可以提升一下的。(在后面可以直接按照这样的模式写方法名和参数而不需要去写实现,SPRING DATA JPA会根据你方法的命名自动转成SQL。(如果配合良好的数据库设计和视图设计,省了不少事情))
&翻译的不错哦!
Spring Data 存储库支持
在我们开始重构该实现之前,看一下示例项目中包含的那些在重构课程中能够运行的测试用例,确保那些代码现在依旧能用。下面我们就来看如何改善我们的实现类。
Spring Data JPA 提供了一个存储库编程模型,首先,每一个受管理的领域对象都要有一个接口:
public interface AccountRepository extends JpaRepository&Account, Long& { … }
定义这个接口是出于两个目的:首先,通过继承JpaRepository我们获得了一组泛型的CRUD方法,使我们能保存Accounts,删除它们等等。其次,这使得Spring Data JPA存储库框架在classpath中扫描该接口,并为它创建一个Spring bean。
&翻译的不错哦!
为了让Spring创建一个实现该接口的bean,你仅需要使用Sping JPA"命名空间"并用适当元素激活其对repository 的支持。
&jpa:repositories base-package="com.acme.repositories" /&
这将扫描包含在包(package)com.acme.repositories下所有的继承于JpaRepository的接口,并为该接口创建实现了SimpleJpaRepository的Sping bean。下面让我们迈出第一步,稍微重构我们的AccountService实现以便使用我们新介绍的repository接口。
@Repository
@Transactional(readOnly = true)
class AccountServiceImpl implements AccountService {
@PersistenceContext
private EntityM
@Autowired
private AccountR
@Transactional
public Account save(Account account) {
return repository.save(account);
public List&Account& findByCustomer(Customer customer) {
TypedQuery query = em.createQuery("select a from Account a where a.customer = ?1", Account.class);
query.setParameter(1, customer);
return query.getResultList();
} 重构之后,我们将save(...)实际委派给repository。在默认情况下,如果一个实体的主键属性为null,那么repository实现会将其作为新建实体,正如你在前面的例子中所看到的一样(注意,如果有必要,你可以对其进行更为详细的控制) 。除此之外,Spring Data JPA repository实现类已经被@Transactional标注的CRUD等方法可以摒弃@Transactional声明。
&翻译的不错哦!
下一步,我们将重构查询方法。并且使查询方法与保存方法遵循相同的委派策略。我们在存储库接口里面引入查询方法并且将我们原有方法委托给新引进的方法:
@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository&Account, Long& {
List&Account& findByCustomer(Customer customer);
@Repository
@Transactional(readOnly = true)
class AccountServiceImpl implements AccountService {
@Autowired
private AccountR
@Transactional
public Account save(Account account) {
return repository.save(account);
public List&Account& findByCustomer(Customer customer) {
return repository.findByCustomer(Customer customer);
我们快速补充点事务处理的知识。在这种非常简单的情况下我们可以从AccountServiceImpl实现类中移除@Transaction注解,因为在存储库的CRUD方法已经进行了事务管理,查询方法在存储库接口中也已经用@Transactional(readOnly = true)进行了标注。当前设置——在service层次标记为事务性的方法是一个最佳实践(尽管在这里并不需要),因为当你在service层次查看方法时它可以显式清楚的表明操作是在同一个事务中处理的。除此之外,如果一个service层次的方法修改了,需要进行多次的存储库方法调用,这种情况下所有的代码任然将会在同一个事务中执行,因为在存储库内部的事务将会简单的加入到外部service层次的事务中。存储库事务行为和调整方式在 中有详细说明。
尝试再次运行测试案例,看看测试是否正常运行。打住,我们还没有实现forfindByCustomer()方法,是不?那它是如何工作的呢?
&翻译的不错哦!
当Spring Data JPA为创建AccountRepository接口创建Spring实例的时候,它会检查接口里面定义的所有查询方法并且 为它们每个都派生一个查询。默认情况下,Spring Data JPA 将自动解析方法名并以此创建一个查询,查询用标准JPA的API实现。在本例中findByCustomer(...)方法在逻辑上等同于JPQL 查询“select a from Account a where a.customer = ?1”。解析方法名称的解析器支持大量的关键字比如And,Or,GreaterThan,LessThan,Like,IsNull,Notand等等,如果您喜欢,您还可以添加ORDER BY子句。有关详情请参阅。这种机制给我们提供了一个查询方法编程模型就像你在Grails 或 Spring Roo中用到的一样。
现在,假设你想要显式的使用指定的查询。要做到这点你可以按照如下命名规约(本例中为Account.findByCustomer)在实体上通过注解或在orm.xml中声明一个JPA命名的查询来实现,另外一个选择就是你可以在存储库方法中使用@Query注解:
@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository&Account, Long& {
@Query("&JPQ statement here&")
List&Account& findByCustomer(Customer customer);
} 现在我们对CustomerServiceImpl用我们已经看到的特性做一个前后对比:
@Repository
@Transactional(readOnly = true)
public class CustomerServiceImpl implements CustomerService {
@PersistenceContext
private EntityM
public Customer findById(Long id) {
return em.find(Customer.class, id);
public List&Customer& findAll() {
return em.createQuery("select c from Customer c", Customer.class).getResultList();
public List&Customer& findAll(int page, int pageSize) {
TypedQuery query = em.createQuery("select c from Customer c", Customer.class);
query.setFirstResult(page * pageSize);
query.setMaxResults(pageSize);
return query.getResultList();
@Transactional
public Customer save(Customer customer) {
// Is new?
if (customer.getId() == null) {
em.persist(customer);
return em.merge(customer);
public List&Customer& findByLastname(String lastname, int page, int pageSize) {
TypedQuery query = em.createQuery("select c from Customer c where c.lastname = ?1", Customer.class);
query.setParameter(1, lastname);
query.setFirstResult(page * pageSize);
query.setMaxResults(pageSize);
return query.getResultList();
} 好吧,首先创建CustomerRepository并消除CRUD方法:
@Transactional(readOnly = true)
public interface CustomerRepository extends JpaRepository&Customer, Long& { … }
@Repository
@Transactional(readOnly = true)
public class CustomerServiceImpl implements CustomerService {
@PersistenceContext
private EntityM
@Autowired
private CustomerR
public Customer findById(Long id) {
return repository.findById(id);
public List&Customer& findAll() {
return repository.findAll();
public List&Customer& findAll(int page, int pageSize) {
TypedQuery query = em.createQuery("select c from Customer c", Customer.class);
query.setFirstResult(page * pageSize);
query.setMaxResults(pageSize);
return query.getResultList();
@Transactional
public Customer save(Customer customer) {
return repository.save(customer);
public List&Customer& findByLastname(String lastname, int page, int pageSize) {
TypedQuery query = em.createQuery("select c from Customer c where c.lastname = ?1", Customer.class);
query.setParameter(1, lastname);
query.setFirstResult(page * pageSize);
query.setMaxResults(pageSize);
return query.getResultList();
&翻译的不错哦!
到目前为止,一切都很好。以下两种方法都可以处理常见的场景:你希望给定查询能够分页而不是返回全部实体(比如一页10条记录)。眼下我们通过两个整数适当限制查询来实现,但这样做有两个问题,两个整数实际上只代表了一个方面,这里并没有明确这一点。除此之外,结果我们只返回了一个简单的list,所以我们丢掉了实际页面的元数据信息:这是第一页吗?这是最后一页吗?总共有多少页?Spring Data提供了一个抽象包括两个接口:Pageable(捕捉分页请求)和Page(捕获结果以及元信息)。让我们按照如下方式尝试添加findByLastname(…)到存储库接口中并重写findAll(…)和findByLastname(…)方法:
@Transactional(readOnly = true)
public interface CustomerRepository extends JpaRepository&Customer, Long& {
Page&Customer& findByLastname(String lastname, Pageable pageable);
public Page&Customer& findAll(Pageable pageable) {
return repository.findAll(pageable);
public Page&Customer& findByLastname(String lastname, Pageable pageable) {
return repository.findByLastname(lastname, pageable);
} 请确保按照名字的变化对测试用例进行了适配,这样它应该能正常运行。归结下来为两件事:我们有CRUD方法支持分页并且查询执行机制知道分页的参数。在这个阶段,我们的包装类实际上已经过时了,因为客户端也可以直接调用我们的存储库接口,这样我们摆脱了整个的实现代码。
&翻译的不错哦!
在本文的课程中,我们把为存储库编写的代码减少到2个接口,包含3个方法一行XML:
@Transactional(readOnly = true)
public interface CustomerRepository extends JpaRepository&Customer, Long& {
Page&Customer& findByLastname(String lastname, Pageable pageable);
@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository&Account, Long& {
List&Account& findByCustomer(Customer customer);
&jpa:repositories base-package="com.acme.repositories" /& 我们拥有类型安全的CRUD方法、查询执行方法和内置的分页功能。这不仅适用于基于JPA的存储库,而且也适用于非关系型数据库,这很酷。第一个支持这些功能的非关系型数据库是MongoDB ,它是几天后发版的Spring Data Document中的一部分。你将会获得Mongo DB版的所有这些特性,而且我们还将支持其它一些数据库。另外,还有其它一些特性等待我们去探秘(比如,实体审计,自定义数据访问代码集成) ,我们会在后续的博文中涉及。
&翻译的不错哦!
spring data 入门CriteriaBuilder cb);  我们只需要重写这个方法即可,相关知识请自行查阅JPA Criteria查询  过滤条件  1:过滤条件会被应用到SQL语句的FROM子句中。在criteria
查询中,查询条件通过Predicate或Expression实例应用到CriteriaQuery对象上。  2:这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上  3:CriteriaBuilder也作为Predicate实例的工厂,通过调用CriteriaBuilder 的条件方法(
equal,notEqual, gt, ge,lt, le,between,like等)创建Predicate对象。  4:复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。  相关代码如下,在这个例子中我们定义了2个类Articel和User类  Article:  & &@Entity  @Table(name = &t_article&)  public class Article implements Serializable{  private static final long serialVersionUID = 1696118L;  @Id  @GeneratedValue  private I  private S  @Temporal(TemporalType.TIMESTAMP)  private Date postT  @Temporal(TemporalType.TIMESTAMP)  private Date lastEditT  private S  private S  private boolean forbidC//禁止评论  @ManyToOne  @JoinColumn(name = &uid&)  private U  priva//是否是推荐  @Temporal(TemporalType.TIMESTAMP)  private Date recommendT//推荐时间  //setter/getter略  }  User:  &&@Entity  @Table(name = &t_user&)  public class User implements Serializable {  private static final long serialVersionUID = 5901053L;  @Id  @GeneratedValue  private I  private S  private S  //setter/getter略  }  其中user和article是一对多的关系,是单向的  封装的查询实体SearchArticle  &&public class SearchArticle implements Serializable{  private static final long serialVersionUID = -6689486L;  private int page = 1;    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = &yyyy-MM-dd
HH:mm:ss&)  private Date postTimeS  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = &yyyy-MM-dd
HH:mm:ss&)  private Date postTimeE  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = &yyyy-MM-dd
HH:mm:ss&)  private Date recTimeS  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = &yyyy-MM-dd
HH:mm:ss&)  private Date recTimeE  private S  下面是查询方法  &&@Autowired  private ArticleRepository articleR  @Override  public QueryResultfindArticle(SearchArticle searchArticle)
{  Sort sort = new Sort(Sort.Direction.DESC,&postTime&);  Specificationspecification = getWhereClause(searchArticle);  Pageall = articleRepository.findAll(specification, new
PageRequest(searchArticle.getPage() - 1, searchArticle.getLimit(),sort));  QueryResultresult = new QueryResult&&();  Listlist = new
ArrayList&&(searchArticle.getLimit());  for (Article article:all.getContent()){  ArticleModel model = new
ArticleModel(article.getAid(),article.getTitle(),article.getPostTime(),article.isRecommend(),  article.getRecommendTime(),article.getIp(),article.getUser().getUid(),article.getUser().getNickname());  list.add(model);  }  result.setRows(list);  result.setTotal(all.getTotalElements());    }  /**  * 动态生成where语句  * @param searchArticle  * @return  */  private SpecificationgetWhereClause(final SearchArticle
searchArticle){  return new Specification() {  @Override  public Predicate toPredicate(Rootroot, CriteriaQuery query,
CriteriaBuilder cb) {  Listpredicate = new ArrayList&&();  if(searchArticle.getPostTimeStart()!=null){  predicate.add(cb.greaterThanOrEqualTo(root.get(&postTime&).as(Date.class),
searchArticle.getPostTimeStart()));  }  if(searchArticle.getPostTimeEnd()!=null){  predicate.add(cb.lessThanOrEqualTo(root.get(&postTime&).as(Date.class),
searchArticle.getPostTimeEnd()));  }  if(searchArticle.getRecTimeStart()!=null){  predicate.add(cb.greaterThanOrEqualTo(root.get(&recommendTime&).as(Date.class),
searchArticle.getRecTimeStart()));  }  if (searchArticle.getRecTimeEnd()!=null){  predicate.add(cb.lessThanOrEqualTo(root.get(&recommendTime&).as(Date.class),
searchArticle.getRecTimeEnd()));  }  if (StringUtils.isNotBlank(searchArticle.getNickname())){  //两张表关联查询  JoinuserJoin =
root.join(root.getModel().getSingularAttribute(&user&,User.class),JoinType.LEFT);  predicate.add(cb.like(userJoin.get(&nickname&).as(String.class), &%& +
searchArticle.getNickname() + &%&));  }  Predicate[] pre = new Predicate[predicate.size()];  return query.where(predicate.toArray(pre)).getRestriction();  }  };  }  其中的 ArticleRepository接口如下,spring data jpa不需要你自己实现dao的接口  1public interface ArticleRepository extends
JpaRepository,JpaSpecificationExecutor{}  通过以上的步骤,我们就能构建相应的查询sql了,使用这个接口要对JPA2.0中Criteria查询有一定的了解
原创文章,转载请注明出处!标签:当前位置: →
→ spring data for jpa 实现多条件排序
spring data for jpa 实现多条件排序
& 作者及来源: yanzhenxing - 博客园 &
&收藏到→_→:
摘要: spring data for jpa 实现多条件排序
"spring data for jpa 实现多条件排序"::
&sort(order... orders);
&sort(list&order&&orders);
&sort(string... properties);
&sort(direction direction, string...&properties);
&sort(direction direction, list&string&&properties);
public&order(direction direction, string property);
direction&和一个列属性,正式我们所要的。
所以采用如下的方法:
&order& orders=new&arraylist&&order&();
&new&order(direction.&asc,&"c"));
&new&order(direction.&desc,&"d"));
&new&pagerequest(pagenumber, pagesize,&new&sort(orders));
搜索此文相关文章:此文来自: 马开东博客
网址: 站长QQ
spring data for jpa 实现多条件排序_博客园相关文章
博客园_总排行榜
博客园_最新
博客园_月排行榜
博客园_周排行榜
博客园_日排行榜spring-data-jpa 复杂查询的写法(包含or的查询) - 推酷
spring-data-jpa 复杂查询的写法(包含or的查询)
记录一下,因为之前遇见的spring-data-jpa相关业务都是很简单的处理掉了。
场景如下:很简单的CMS常用查询,栏目下有多个子栏目,子栏目有包含内容。
public class Channel{
private String parentI//所有的父节点,简化查询策略 例如 0,1,11,
private List&Channel& channels = Lists.newArrayList(); //所有的儿子栏目
public class Content{
private C //属于哪个栏目
所以比如有如下结构时:栏目A下有两个子栏目B,C;这三个栏目下都有各自的内容, 那么当查询A下的内容时,肯定也是要包含B,C栏目下的内容。
比如说A栏目的id是 10 ,再加上别的查询条件的话, 也就是说 sql 语句肯定是类似:
select * from Content as s where (s.channel.id = 10 or s.channel.parentIds like '%,10,%') and s.yyy = 'xxxxx' and s.xxx = 'yyyy';
想来想去发现实在没有简单的办法做or查询之后跟动态的and查询参数,好像只能自己构造,所以在此记录一下,避免忘记。
此处动态的and 查询条件构造是使用了springside的方法。并且我不知道这样的构造方法是不是spring-data-jpa的标准做法,如果不是,请指正
* 查询某个栏目下的所有文章,并且分页。
* 如该栏目下有子栏目,则需要一起显示
* @param channelId 栏目id
* @param searchParams
* @param pageNumber
* @param pageSize
public Page&Content& getContentListPaged(final Integer channelId,final Collection&SearchFilter& filters,int pageNumber, int pageSize){
return contentDao.findAll(new Specification&Content&(){
public Predicate toPredicate(Root&Content& root,
CriteriaQuery&?& query, CriteriaBuilder builder) {
//path转化
List&Predicate& orPredicates = Lists.newArrayList();
Path&String& idPath = root.get(&channel&).get(&id&);
Path&String& parentIdsPath = root.get(&channel&).get(&parentIds&);
Predicate p1 = builder.equal(root.get(&channel&).get(&id&), channelId);
orPredicates.add(builder.or(p1));
Predicate p2 = builder.like((Path)root.get(&channel&).get(&parentIds&), &%,& + channelId + &,%&);
orPredicates.add(builder.or(p2));
//以下是springside3提供的方法
Predicate o = DynamicSpecifications.bySearchFilter(filters, Content.class).toPredicate(root, query, builder);
Predicate p = builder.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
query.where(p,o);
}, new PageRequest(pageNumber - 1, pageSize));
实际查询的输出sql如下,where之前省略:
content0_.channel_id=channel1_.id
content0_.channel_id=21
or channel1_.parentIds like ?
content0_.title like ?
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 spring jpa 自定义sql 的文章

 

随机推荐