关于 Spring For All

关于 Spring For All

Spring For All 的一切
最新动态

最新动态

Spring 5 会是咋样呢
Spring Boot

Spring Boot

快速构建并运行 Spring 应用程序
Spring Cloud

Spring Cloud

分布式系统的一套工具,可用于构建微服务
Spring Framework

Spring Framework

提供依赖注入、事务、Web应用、数据访问等模块
Spring Data

Spring Data

提供一致性数据访问模块
Spring Security

Spring Security

提供应用身份验证和授权支持
Spring Batch

Spring Batch

提供高容批处理操作模块
Spring AMQP

Spring AMQP

基于AMQP消息解决方案
Micro Service Arch.

Micro Service Arch.

微服务架构相关
开源项目及视频教程

开源项目及视频教程

做中国最好的 Spring 开源项目及视频教程
小马哥专栏

小马哥专栏

阿里技术布道者,专注 Spring Boot 及微服务

Spring Data JPA系列:数据更新(Update)

wayne 发表了文章 • 0 个评论 • 481 次浏览 • 2017-07-11 11:15 • 来自相关话题

上次通过《Spring Data JPA系列:使用@Modifying修改(Modifying queries)》介绍了数据更新的方式,这种更新方式会很不方便,写的时候也比较麻烦,可以为更新密码、更新用户名等一些特殊的更新单独定义,但是对大多数数据操作是不方便的,比如我要更新一条有一百个字段的数据,这时候如果要通过Modifying方式就非常的不方便,因此,我们需要一种新的方式来解救。
通过阅读Spring-Data-JPA相关的文档和博客,找到了对应的解决方案,就是使用`save()`方法,经过测试,可用。
我们平时对`save()`方法的理解,大多是等同于`insert()`,主要是指新增一条数据,而JPA的`save()`方法包含了`merge()`的概念,就是说,如果save的对象不存在primary key或者primary key值在database内不存在的时候会新添加一条数据,如果primary key 存在并且primary key已经在database中存在,那就会依据primary key对该条数据进行更新,这是我们乐意见到的。

参考的文章:```java
https://stackoverflow.com/ques ... a-jpa
```

相关描述如下:

Identity of entities is defined by their primary keys. Since firstname and lastname are not parts of the primary key, you cannot tell JPA to treat Users with the same firstnames and lastnames as equal if they have different userIds.

So, if you want to update a User identified by its firstname and lastname, you need to find that User by a query, and then change appropriate fields of the object your found. These changes will be flushed to the database automatically at the end of transaction, so that you don't need to do anything to save these changes explicitly.

##### EDIT:

Perhaps I should elaborate on overall semantics of JPA. There are two main approaches to design of persistence APIs:

- insert/update approach. When you need to modify the database you should call methods of persistence API explicitly: you call insert to insert an object, or update to save new state of the object to the database.
- Unit of Work approach. In this case you have a set of objects managed by persistence library. All changes you make to these objects will be flushed to the database automatically at the end of Unit of Work (i.e. at the end of the current transaction in typical case). When you need to insert new record to the database, you make the corresponding object managed. Managed objects are identified by their primary keys, so that if you make an object with predefined primary key managed, it will be associated with the database record of the same id, and state of this object will be propagated to that record automatically.

JPA follows the later approach. save() in Spring Data JPA is backed by merge() in plain JPA, therefore it makes your entity managed as described above. It means that calling save() on an object with predefined id will update the corresponding database record rather than insert a new one, and also explains why save() is not called create().
 
 
参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com

版权归作者所有,如需转载,请与作者联系! 查看全部
上次通过《Spring Data JPA系列:使用@Modifying修改(Modifying queries)》介绍了数据更新的方式,这种更新方式会很不方便,写的时候也比较麻烦,可以为更新密码、更新用户名等一些特殊的更新单独定义,但是对大多数数据操作是不方便的,比如我要更新一条有一百个字段的数据,这时候如果要通过Modifying方式就非常的不方便,因此,我们需要一种新的方式来解救。
通过阅读Spring-Data-JPA相关的文档和博客,找到了对应的解决方案,就是使用`save()`方法,经过测试,可用。
我们平时对`save()`方法的理解,大多是等同于`insert()`,主要是指新增一条数据,而JPA的`save()`方法包含了`merge()`的概念,就是说,如果save的对象不存在primary key或者primary key值在database内不存在的时候会新添加一条数据,如果primary key 存在并且primary key已经在database中存在,那就会依据primary key对该条数据进行更新,这是我们乐意见到的。

参考的文章:```java
https://stackoverflow.com/ques ... a-jpa
```

相关描述如下:

Identity of entities is defined by their primary keys. Since firstname and lastname are not parts of the primary key, you cannot tell JPA to treat Users with the same firstnames and lastnames as equal if they have different userIds.

So, if you want to update a User identified by its firstname and lastname, you need to find that User by a query, and then change appropriate fields of the object your found. These changes will be flushed to the database automatically at the end of transaction, so that you don't need to do anything to save these changes explicitly.

##### EDIT:

Perhaps I should elaborate on overall semantics of JPA. There are two main approaches to design of persistence APIs:

- insert/update approach. When you need to modify the database you should call methods of persistence API explicitly: you call insert to insert an object, or update to save new state of the object to the database.
- Unit of Work approach. In this case you have a set of objects managed by persistence library. All changes you make to these objects will be flushed to the database automatically at the end of Unit of Work (i.e. at the end of the current transaction in typical case). When you need to insert new record to the database, you make the corresponding object managed. Managed objects are identified by their primary keys, so that if you make an object with predefined primary key managed, it will be associated with the database record of the same id, and state of this object will be propagated to that record automatically.

JPA follows the later approach. save() in Spring Data JPA is backed by merge() in plain JPA, therefore it makes your entity managed as described above. It means that calling save() on an object with predefined id will update the corresponding database record rather than insert a new one, and also explains why save() is not called create().
 
 
参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com


版权归作者所有,如需转载,请与作者联系!


Spring Data JPA系列:投影(Projection)的用法

wayne 发表了文章 • 0 个评论 • 356 次浏览 • 2017-07-09 23:58 • 来自相关话题

在JPA的查询中,有一个不方便的地方,@Query注解,如果查询直接是$(document).ready(function() {$('pre code').each(function(i, block) { hljs.highlightBlock( block); }); });Select C from Customer c,这时候,查询的返回对象就是Customer这个完整的对象,包含所有字段,对于我们的示例并没有什么问题,但是对于比较庞大的domain类,这个查询时就比较要命,并不是所有的字段都能用到,比较头疼。另外,如果定义select c.firstName as firstName,c.lastName as lastName from Customer c这个查询结果,返回的对象是Object类型,而且无法直接转换成Customer对象,这样用起来就不是很方便。
对于这种情况,JPA提供了一种声明方式来解决,即声明一个接口类,然后直接使用这个接口类接受返回的数据即可。下面奉上代码:

1、增加CustomerProjection接口类
package com.example.demo.dto;

import org.springframework.beans.factory.annotation.Value;

/**
* Created by Administrator on 2017/7/9 0009.
*/
public interface CustomerProjection {
@Value("#{target.firstName + ' ' + target.lastName}")
String getFullName();

String getFirstName();

String getLastName();
}
这里声明的方式是可以直接通过get+属性名,这是普通的,另外也可以通过@Value注解来实现指定字段,除了指定字段也可以做聚合展示,比如有些地方需要展示客户的全名,这里定义的getFullName()方法及注解@Value即完成这一操作。需要注意这里的@Value中的target表达式写法及拼接方法。
 
2、增加CustomerRepository方法
@Query("SELECT c.firstName as firstName,c.lastName as lastName from Customer c")
Collection<CustomerProjection> findAllProjectedBy();3、增加CustomerController方法
/**
* find by projections
*/
@RequestMapping("/findAllProjections")
public void findAllProjections(){
Collection<CustomerProjection> projections = repository.findAllProjectedBy();
System.out.println(projections);
System.out.println(projections.size());
for (CustomerProjection projection:projections){
System.out.println("FullName:"+projection.getFullName());
System.out.println("FirstName:"+projection.getFirstName());
System.out.println("LastName:"+projection.getLastName());
}
}这里只是做了简单示意,深入的内容需要自己去挖掘探索。不过关于Projection的资料比较少,我也是扒了不少资料才理解的差不多了,还需要多多实践。

另外spring-data-examples项目中有一些JPA的例子,可以用来学习,梳理思路。https://github.com/spring-projects/spring-data-examples/tree/master/jpa
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com
 

版权归作者所有,如需转载,请与作者联系! 查看全部
在JPA的查询中,有一个不方便的地方,@Query注解,如果查询直接是
Select C from Customer c
,这时候,查询的返回对象就是Customer这个完整的对象,包含所有字段,对于我们的示例并没有什么问题,但是对于比较庞大的domain类,这个查询时就比较要命,并不是所有的字段都能用到,比较头疼。另外,如果定义
select c.firstName as firstName,c.lastName as lastName from Customer c
这个查询结果,返回的对象是Object类型,而且无法直接转换成Customer对象,这样用起来就不是很方便。
对于这种情况,JPA提供了一种声明方式来解决,即声明一个接口类,然后直接使用这个接口类接受返回的数据即可。下面奉上代码:

1、增加CustomerProjection接口类
package com.example.demo.dto;

import org.springframework.beans.factory.annotation.Value;

/**
* Created by Administrator on 2017/7/9 0009.
*/
public interface CustomerProjection {
@Value("#{target.firstName + ' ' + target.lastName}")
String getFullName();

String getFirstName();

String getLastName();
}
这里声明的方式是可以直接通过get+属性名,这是普通的,另外也可以通过@Value注解来实现指定字段,除了指定字段也可以做聚合展示,比如有些地方需要展示客户的全名,这里定义的getFullName()方法及注解@Value即完成这一操作。需要注意这里的@Value中的target表达式写法及拼接方法。
 
2、增加CustomerRepository方法
    @Query("SELECT c.firstName as firstName,c.lastName as lastName from Customer  c")
Collection<CustomerProjection> findAllProjectedBy();
3、增加CustomerController方法
    /**
* find by projections
*/
@RequestMapping("/findAllProjections")
public void findAllProjections(){
Collection<CustomerProjection> projections = repository.findAllProjectedBy();
System.out.println(projections);
System.out.println(projections.size());
for (CustomerProjection projection:projections){
System.out.println("FullName:"+projection.getFullName());
System.out.println("FirstName:"+projection.getFirstName());
System.out.println("LastName:"+projection.getLastName());
}
}
这里只是做了简单示意,深入的内容需要自己去挖掘探索。不过关于Projection的资料比较少,我也是扒了不少资料才理解的差不多了,还需要多多实践。

另外spring-data-examples项目中有一些JPA的例子,可以用来学习,梳理思路。https://github.com/spring-projects/spring-data-examples/tree/master/jpa
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com
 


版权归作者所有,如需转载,请与作者联系!


Spring Data JPA系列:分页(Pageable)

wayne 发表了文章 • 0 个评论 • 337 次浏览 • 2017-07-09 23:54 • 来自相关话题

在JPA中提供了很方便的分页功能,那就是Pageable(org.springframework.data.domain.Pageable)以及它的实现类PageRequest(org.springframework.data.domain.PageRequest),详细的可以见示例代码。
1、改变CustomerRepository方法​ /**
* 一个参数,匹配两个字段
* @param name2
* @Param pageable 分页参数
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
* 这里增加了@QueryHints注解,是给查询添加一些额外的提示
* 比如当前的name值为HINT_COMMENT是在查询的时候带上一些备注信息
*/
@QueryHints(value = { @QueryHint(name = HINT_COMMENT, value = "a query for pageable")})
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name")
Page<Customer> findByName(@Param("name") String name2,Pageable pageable);
2、增加CustomerController方法pageable
/**
* 分页
* 应用查询提示@QueryHints,这里是在查询的适合增加了一个comment
* 查询结果是lastName和firstName都是bauer这个值的数据
*/
@RequestMapping("/pageable")
public void pageable(){
//Pageable是接口,PageRequest是接口实现
//PageRequest的对象构造函数有多个,page是页数,初始值是0,size是查询结果的条数,后两个参数参考Sort对象的构造方法
Pageable pageable = new PageRequest(0,3, Sort.Direction.DESC,"id");
Page<Customer> page = repository.findByName("bauer",pageable);
//查询结果总行数
System.out.println(page.getTotalElements());
//按照当前分页大小,总页数
System.out.println(page.getTotalPages());
//按照当前页数、分页大小,查出的分页结果集合
for (Customer customer: page.getContent()) {
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}从示例代码的注释当中可以看到Page对象的相关参数及值的说明,更详细的用法,参考PageRequest源码。

小结:怎么样,是不是很简单很方便?!
另:PageRequest.java源码在上一篇文章已经有了。
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com
 

版权归作者所有,如需转载,请与作者联系! 查看全部
在JPA中提供了很方便的分页功能,那就是Pageable(org.springframework.data.domain.Pageable)以及它的实现类PageRequest(org.springframework.data.domain.PageRequest),详细的可以见示例代码。
1、改变CustomerRepository方法​
    /**
* 一个参数,匹配两个字段
* @param name2
* @Param pageable 分页参数
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
* 这里增加了@QueryHints注解,是给查询添加一些额外的提示
* 比如当前的name值为HINT_COMMENT是在查询的时候带上一些备注信息
*/
@QueryHints(value = { @QueryHint(name = HINT_COMMENT, value = "a query for pageable")})
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name")
Page<Customer> findByName(@Param("name") String name2,Pageable pageable);
2、增加CustomerController方法pageable
    /**
* 分页
* 应用查询提示@QueryHints,这里是在查询的适合增加了一个comment
* 查询结果是lastName和firstName都是bauer这个值的数据
*/
@RequestMapping("/pageable")
public void pageable(){
//Pageable是接口,PageRequest是接口实现
//PageRequest的对象构造函数有多个,page是页数,初始值是0,size是查询结果的条数,后两个参数参考Sort对象的构造方法
Pageable pageable = new PageRequest(0,3, Sort.Direction.DESC,"id");
Page<Customer> page = repository.findByName("bauer",pageable);
//查询结果总行数
System.out.println(page.getTotalElements());
//按照当前分页大小,总页数
System.out.println(page.getTotalPages());
//按照当前页数、分页大小,查出的分页结果集合
for (Customer customer: page.getContent()) {
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}
从示例代码的注释当中可以看到Page对象的相关参数及值的说明,更详细的用法,参考PageRequest源码。

小结:怎么样,是不是很简单很方便?!
另:PageRequest.java源码在上一篇文章已经有了。
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com
 


版权归作者所有,如需转载,请与作者联系!


Spring Data Jpa遇到mysql关键字

采蘑菇的大叔 发表了文章 • 0 个评论 • 306 次浏览 • 2017-07-08 16:09 • 来自相关话题

今天来加班,同事发来一张图:

分析知道,这是因为spring data jpa遇到了mysql的关键字,导致update失败了。
查看了model中,是这样写的。[code]/**
* 参数名
*/
@Column(name = "name")
private String name;
[/code]
寻求了一会儿方案,突发奇想,改成下面的样子[code]/**
* 参数名
*/
@Column(name = "`name`")
private String name;
[/code]
运行,解决。

没有用过hibernate,bibernate里面以前应该有这种问题的方案吧。

查看全部
今天来加班,同事发来一张图:

分析知道,这是因为spring data jpa遇到了mysql的关键字,导致update失败了。
查看了model中,是这样写的。
[code]/**
* 参数名
*/
@Column(name = "name")
private String name;
[/code]
寻求了一会儿方案,突发奇想,改成下面的样子
[code]/**
* 参数名
*/
@Column(name = "`name`")
private String name;
[/code]
运行,解决。


没有用过hibernate,bibernate里面以前应该有这种问题的方案吧。


Redis客户端推荐?

xiaobaxi 回复了问题 • 2 人关注 • 1 个回复 • 257 次浏览 • 2017-07-07 09:43 • 来自相关话题

Spring Data JPA系列:应用查询提示(Applying query hints)

wayne 发表了文章 • 0 个评论 • 581 次浏览 • 2017-07-06 10:54 • 来自相关话题

这一节讲应用查询提示,学这一节比较波折,文档上的介绍太简单了,而且面对示例不知道如何下手,所以拖了一下,才有点头绪。
public interface UserRepository extends Repository<User, Long> {

@QueryHints(value = { @QueryHint(name = "name", value = "value")},
forCounting = false)
Page<User> findByLastname(String lastname, Pageable pageable);
}对name和value的值我一直以为是自定义的,最后发现不完全是,是有一组定义好的值供我们选择。
1、QueryHints源码如下:
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa;

import java.util.HashSet;
import java.util.Set;

import static org.hibernate.annotations.QueryHints.CACHEABLE;
import static org.hibernate.annotations.QueryHints.CACHE_MODE;
import static org.hibernate.annotations.QueryHints.CACHE_REGION;
import static org.hibernate.annotations.QueryHints.COMMENT;
import static org.hibernate.annotations.QueryHints.FETCHGRAPH;
import static org.hibernate.annotations.QueryHints.FETCH_SIZE;
import static org.hibernate.annotations.QueryHints.FLUSH_MODE;
import static org.hibernate.annotations.QueryHints.LOADGRAPH;
import static org.hibernate.annotations.QueryHints.NATIVE_LOCKMODE;
import static org.hibernate.annotations.QueryHints.READ_ONLY;
import static org.hibernate.annotations.QueryHints.TIMEOUT_HIBERNATE;
import static org.hibernate.annotations.QueryHints.TIMEOUT_JPA;

/**
* Defines the supported JPA query hints
*
* @author Steve Ebersole
*/
public class QueryHints {
/**
* The hint key for specifying a query timeout per Hibernate O/RM, which defines the timeout in seconds.
*
* @deprecated use {@link #SPEC_HINT_TIMEOUT} instead
*/
@Deprecated
public static final String HINT_TIMEOUT = TIMEOUT_HIBERNATE;

/**
* The hint key for specifying a query timeout per JPA, which defines the timeout in milliseconds
*/
public static final String SPEC_HINT_TIMEOUT = TIMEOUT_JPA;

/**
* The hint key for specifying a comment which is to be embedded into the SQL sent to the database.
*/
public static final String HINT_COMMENT = COMMENT;

/**
* The hint key for specifying a JDBC fetch size, used when executing the resulting SQL.
*/
public static final String HINT_FETCH_SIZE = FETCH_SIZE;

/**
* The hint key for specifying whether the query results should be cached for the next (cached) execution of the
* "same query".
*/
public static final String HINT_CACHEABLE = CACHEABLE;

/**
* The hint key for specifying the name of the cache region (within Hibernate's query result cache region)
* to use for storing the query results.
*/
public static final String HINT_CACHE_REGION = CACHE_REGION;

/**
* The hint key for specifying that objects loaded into the persistence context as a result of this query execution
* should be associated with the persistence context as read-only.
*/
public static final String HINT_READONLY = READ_ONLY;

/**
* The hint key for specifying the cache mode ({@link org.hibernate.CacheMode}) to be in effect for the
* execution of the hinted query.
*/
public static final String HINT_CACHE_MODE = CACHE_MODE;

/**
* The hint key for specifying the flush mode ({@link org.hibernate.FlushMode}) to be in effect for the
* execution of the hinted query.
*/
public static final String HINT_FLUSH_MODE = FLUSH_MODE;

public static final String HINT_NATIVE_LOCKMODE = NATIVE_LOCKMODE;

/**
* Hint providing a "fetchgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
* FetchType.EAGER (via join fetch or subsequent select).
*
* Note: Currently, attributes that are not specified are treated as FetchType.LAZY or FetchType.EAGER depending
* on the attribute's definition in metadata, rather than forcing FetchType.LAZY.
*/
public static final String HINT_FETCHGRAPH = FETCHGRAPH;

/**
* Hint providing a "loadgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
* FetchType.EAGER (via join fetch or subsequent select). Attributes that are not specified are treated as
* FetchType.LAZY or FetchType.EAGER depending on the attribute's definition in metadata
*/
public static final String HINT_LOADGRAPH = LOADGRAPH;

private static final Set<String> HINTS = buildHintsSet();

private static Set<String> buildHintsSet() {
HashSet<String> hints = new HashSet<String>();
hints.add( HINT_TIMEOUT );
hints.add( SPEC_HINT_TIMEOUT );
hints.add( HINT_COMMENT );
hints.add( HINT_FETCH_SIZE );
hints.add( HINT_CACHE_REGION );
hints.add( HINT_CACHEABLE );
hints.add( HINT_READONLY );
hints.add( HINT_CACHE_MODE );
hints.add( HINT_FLUSH_MODE );
hints.add( HINT_NATIVE_LOCKMODE );
hints.add( HINT_FETCHGRAPH );
hints.add( HINT_LOADGRAPH );
return java.util.Collections.unmodifiableSet( hints );
}

public static Set<String> getDefinedHints() {
return HINTS;
}

protected QueryHints() {
}
}对应值的介绍,除了注释,我在网上也找到了一些资料,讲的很详细:
https://www.thoughts-on-java.org/11-jpa-hibernate-query-hints-every-developer-know/
https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/jpa/QueryHints.html
 
2、在CustomerRepository中添加示例:

导入QueryHint的name的对应的值import static org.hibernate.jpa.QueryHints.HINT_COMMENT; /**
* 一个参数,匹配两个字段
* @param name2
* @Param pageable 分页参数
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
* 这里增加了@QueryHints注解,是给查询添加一些额外的提示
* 比如当前的name值为HINT_COMMENT是在查询的时候带上一些备注信息
*/
@QueryHints(value = { @QueryHint(name = HINT_COMMENT, value = "a query for pageable")})
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name")
Page<Customer> findByName(@Param("name") String name2,Pageable pageable);
3、在CustomerController中添加示例:
/**
* 分页
* 应用查询提示@QueryHints,这里是在查询的适合增加了一个comment
* 查询结果是lastName和firstName都是bauer这个值的数据
*/
@RequestMapping("/pageable")
public void pageable(){
//Pageable是接口,PageRequest是接口实现
//PageRequest的对象构造函数有多个,page是页数,初始值是0,size是查询结果的条数,后两个参数参考Sort对象的构造方法
Pageable pageable = new PageRequest(0,3, Sort.Direction.DESC,"id");
Page<Customer> page = repository.findByName("bauer",pageable);
//查询结果总行数
System.out.println(page.getTotalElements());
//按照当前分页大小,总页数
System.out.println(page.getTotalPages());
//按照当前页数、分页大小,查出的分页结果集合
for (Customer customer: page.getContent()) {
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}注意到,这里除了方法调用了带有查询提示的方法以外,还对方法的Pageable参数进行了简单实现——PageRequest,这个类包含了多个构造函数,可以根据自己的需求自由定制,对于排序不分参考Sort那一篇。
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com 查看全部

这一节讲应用查询提示,学这一节比较波折,文档上的介绍太简单了,而且面对示例不知道如何下手,所以拖了一下,才有点头绪。
public interface UserRepository extends Repository<User, Long> {

@QueryHints(value = { @QueryHint(name = "name", value = "value")},
forCounting = false)
Page<User> findByLastname(String lastname, Pageable pageable);
}
对name和value的值我一直以为是自定义的,最后发现不完全是,是有一组定义好的值供我们选择。
1、QueryHints源码如下:
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa;

import java.util.HashSet;
import java.util.Set;

import static org.hibernate.annotations.QueryHints.CACHEABLE;
import static org.hibernate.annotations.QueryHints.CACHE_MODE;
import static org.hibernate.annotations.QueryHints.CACHE_REGION;
import static org.hibernate.annotations.QueryHints.COMMENT;
import static org.hibernate.annotations.QueryHints.FETCHGRAPH;
import static org.hibernate.annotations.QueryHints.FETCH_SIZE;
import static org.hibernate.annotations.QueryHints.FLUSH_MODE;
import static org.hibernate.annotations.QueryHints.LOADGRAPH;
import static org.hibernate.annotations.QueryHints.NATIVE_LOCKMODE;
import static org.hibernate.annotations.QueryHints.READ_ONLY;
import static org.hibernate.annotations.QueryHints.TIMEOUT_HIBERNATE;
import static org.hibernate.annotations.QueryHints.TIMEOUT_JPA;

/**
* Defines the supported JPA query hints
*
* @author Steve Ebersole
*/
public class QueryHints {
/**
* The hint key for specifying a query timeout per Hibernate O/RM, which defines the timeout in seconds.
*
* @deprecated use {@link #SPEC_HINT_TIMEOUT} instead
*/
@Deprecated
public static final String HINT_TIMEOUT = TIMEOUT_HIBERNATE;

/**
* The hint key for specifying a query timeout per JPA, which defines the timeout in milliseconds
*/
public static final String SPEC_HINT_TIMEOUT = TIMEOUT_JPA;

/**
* The hint key for specifying a comment which is to be embedded into the SQL sent to the database.
*/
public static final String HINT_COMMENT = COMMENT;

/**
* The hint key for specifying a JDBC fetch size, used when executing the resulting SQL.
*/
public static final String HINT_FETCH_SIZE = FETCH_SIZE;

/**
* The hint key for specifying whether the query results should be cached for the next (cached) execution of the
* "same query".
*/
public static final String HINT_CACHEABLE = CACHEABLE;

/**
* The hint key for specifying the name of the cache region (within Hibernate's query result cache region)
* to use for storing the query results.
*/
public static final String HINT_CACHE_REGION = CACHE_REGION;

/**
* The hint key for specifying that objects loaded into the persistence context as a result of this query execution
* should be associated with the persistence context as read-only.
*/
public static final String HINT_READONLY = READ_ONLY;

/**
* The hint key for specifying the cache mode ({@link org.hibernate.CacheMode}) to be in effect for the
* execution of the hinted query.
*/
public static final String HINT_CACHE_MODE = CACHE_MODE;

/**
* The hint key for specifying the flush mode ({@link org.hibernate.FlushMode}) to be in effect for the
* execution of the hinted query.
*/
public static final String HINT_FLUSH_MODE = FLUSH_MODE;

public static final String HINT_NATIVE_LOCKMODE = NATIVE_LOCKMODE;

/**
* Hint providing a "fetchgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
* FetchType.EAGER (via join fetch or subsequent select).
*
* Note: Currently, attributes that are not specified are treated as FetchType.LAZY or FetchType.EAGER depending
* on the attribute's definition in metadata, rather than forcing FetchType.LAZY.
*/
public static final String HINT_FETCHGRAPH = FETCHGRAPH;

/**
* Hint providing a "loadgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
* FetchType.EAGER (via join fetch or subsequent select). Attributes that are not specified are treated as
* FetchType.LAZY or FetchType.EAGER depending on the attribute's definition in metadata
*/
public static final String HINT_LOADGRAPH = LOADGRAPH;

private static final Set<String> HINTS = buildHintsSet();

private static Set<String> buildHintsSet() {
HashSet<String> hints = new HashSet<String>();
hints.add( HINT_TIMEOUT );
hints.add( SPEC_HINT_TIMEOUT );
hints.add( HINT_COMMENT );
hints.add( HINT_FETCH_SIZE );
hints.add( HINT_CACHE_REGION );
hints.add( HINT_CACHEABLE );
hints.add( HINT_READONLY );
hints.add( HINT_CACHE_MODE );
hints.add( HINT_FLUSH_MODE );
hints.add( HINT_NATIVE_LOCKMODE );
hints.add( HINT_FETCHGRAPH );
hints.add( HINT_LOADGRAPH );
return java.util.Collections.unmodifiableSet( hints );
}

public static Set<String> getDefinedHints() {
return HINTS;
}

protected QueryHints() {
}
}
对应值的介绍,除了注释,我在网上也找到了一些资料,讲的很详细:
https://www.thoughts-on-java.org/11-jpa-hibernate-query-hints-every-developer-know/
https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/jpa/QueryHints.html
 
2、在CustomerRepository中添加示例:

导入QueryHint的name的对应的值
import static org.hibernate.jpa.QueryHints.HINT_COMMENT;
    /**
* 一个参数,匹配两个字段
* @param name2
* @Param pageable 分页参数
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
* 这里增加了@QueryHints注解,是给查询添加一些额外的提示
* 比如当前的name值为HINT_COMMENT是在查询的时候带上一些备注信息
*/
@QueryHints(value = { @QueryHint(name = HINT_COMMENT, value = "a query for pageable")})
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name")
Page<Customer> findByName(@Param("name") String name2,Pageable pageable);
3、在CustomerController中添加示例:
    /**
* 分页
* 应用查询提示@QueryHints,这里是在查询的适合增加了一个comment
* 查询结果是lastName和firstName都是bauer这个值的数据
*/
@RequestMapping("/pageable")
public void pageable(){
//Pageable是接口,PageRequest是接口实现
//PageRequest的对象构造函数有多个,page是页数,初始值是0,size是查询结果的条数,后两个参数参考Sort对象的构造方法
Pageable pageable = new PageRequest(0,3, Sort.Direction.DESC,"id");
Page<Customer> page = repository.findByName("bauer",pageable);
//查询结果总行数
System.out.println(page.getTotalElements());
//按照当前分页大小,总页数
System.out.println(page.getTotalPages());
//按照当前页数、分页大小,查出的分页结果集合
for (Customer customer: page.getContent()) {
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}
注意到,这里除了方法调用了带有查询提示的方法以外,还对方法的Pageable参数进行了简单实现——PageRequest,这个类包含了多个构造函数,可以根据自己的需求自由定制,对于排序不分参考Sort那一篇。
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com

Spring Data JPA系列:使用@Modifying修改(Modifying queries)

wayne 发表了文章 • 0 个评论 • 365 次浏览 • 2017-07-04 15:59 • 来自相关话题

通过之前的讲解和示例,我们掌握了基本的JPA使用方法,大多数是一些查询数据的方法,这一节我们学习通过@Modifying去做数据更新的方法示例。

1、在CustomerRepository上增加新方法
/**
* 根据lastName去更新firstName,返回结果是更改数据的行数
* @param firstName
* @param lastName
* @return
*/
@Modifying//更新查询
@Transactional//开启事务
@Query("update Customer c set c.firstName = ?1 where c.lastName = ?2")
int setFixedFirstnameFor(String firstName, String lastName);这里需要注意,在使用@Modifying注解的时候,一定要加上事务注解@Transactional,如果你忘了或者加错了,那很可能报如下错误:
2017-07-01 01:03:24.844 ERROR 8072 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]
: Servlet.service() for servlet [dispatcherServlet] in context with path []
threw exception [Request processing failed; nested exception is org.springframework
dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is
javax.persistence.TransactionRequiredException: Executing an update/delete query] with root cause
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:54)
~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]如果你发现了上述错误就去加上事务吧。
 
2、在CustomerController中测试:
/**
* 根据FirstName进行修改
*/
@RequestMapping("/modifying")
public void modifying(){
Integer result = repository.setFixedFirstnameFor("Bauorx","Bauer");
if(result!=null){
System.out.println("modifying result:"+result);
}
System.out.println("-------------------------------------------");

}
这个示例是update的,还有delete也是同样的,参考上述示例。
 
到此,@Modifying的介绍几本就这样了。
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com 查看全部
通过之前的讲解和示例,我们掌握了基本的JPA使用方法,大多数是一些查询数据的方法,这一节我们学习通过@Modifying去做数据更新的方法示例。

1、在CustomerRepository上增加新方法
    /**
* 根据lastName去更新firstName,返回结果是更改数据的行数
* @param firstName
* @param lastName
* @return
*/
@Modifying//更新查询
@Transactional//开启事务
@Query("update Customer c set c.firstName = ?1 where c.lastName = ?2")
int setFixedFirstnameFor(String firstName, String lastName);
这里需要注意,在使用@Modifying注解的时候,一定要加上事务注解@Transactional,如果你忘了或者加错了,那很可能报如下错误:
2017-07-01 01:03:24.844 ERROR 8072 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    
: Servlet.service() for servlet [dispatcherServlet] in context with path []
threw exception [Request processing failed; nested exception is org.springframework
dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is
javax.persistence.TransactionRequiredException: Executing an update/delete query] with root cause
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:54)
~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
如果你发现了上述错误就去加上事务吧。
 
2、在CustomerController中测试:
    /**
* 根据FirstName进行修改
*/
@RequestMapping("/modifying")
public void modifying(){
Integer result = repository.setFixedFirstnameFor("Bauorx","Bauer");
if(result!=null){
System.out.println("modifying result:"+result);
}
System.out.println("-------------------------------------------");

}

这个示例是update的,还有delete也是同样的,参考上述示例。
 
到此,@Modifying的介绍几本就这样了。
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com

Spring Data JPA系列:使用Sort进行排序(Using Sort)

wayne 发表了文章 • 0 个评论 • 327 次浏览 • 2017-07-01 12:45 • 来自相关话题

通过上一节的学习,我们知道了如何用@Query注解来实现灵活的查询。在上一节的示例中,我也尝试给出简单的排序,通过JPQL语句以及原生SQL来实现的。这样的实现,虽然在一定程度上可以应用,但是灵活度不够,因此结合@Query注解,我们可以使用Sort来对结果进行排序。

1、在CustomerRepository内添加方法
/**
* 一个参数,匹配两个字段
* @param name2
* @param sort 指定排序的参数,可以根据需要进行调整
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
*
*/
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name")
List<Customer> findByName4(@Param("name") String name2,Sort sort);方法一如既往,是声明式的,只是在原有方法的基础上,加上Sort(org.springframework.data.domain.Sort)作为参数即可。
 
2、在CustomerController中测试
/**
* @Query注解方式查询,
* 用@Param指定参数,匹配firstName和lastName
*/
@RequestMapping("/findByName")
public void findByName4(){
//按照ID倒序排列
System.out.println("直接创建sort对象,通过排序方法和属性名");
Sort sort = new Sort(Sort.Direction.DESC,"id");
List<Customer> result = repository.findByName4("Bauer",sort);
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
//按照ID倒序排列
System.out.println("通过Sort.Order对象创建sort对象");
Sort sortx = new Sort(new Sort.Order(Sort.Direction.DESC,"id"));
List<Customer> resultx = repository.findByName4("Bauer",sort);
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");

System.out.println("通过排序方法和属性List创建sort对象");
List<String> sortProperties = new ArrayList<>();
sortProperties.add("id");
sortProperties.add("firstName");
Sort sort2 = new Sort(Sort.Direction.DESC,sortProperties);
List<Customer> result2 = repository.findByName4("Bauer",sort2);
for (Customer customer:result2){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");

System.out.println("通过创建Sort.Order对象的集合创建sort对象");
List<Sort.Order> orders = new ArrayList<>();
orders.add(new Sort.Order(Sort.Direction.DESC,"id"));
orders.add(new Sort.Order(Sort.Direction.ASC,"firstName"));
List<Customer> result3 = repository.findByName4("Bauer",new Sort(orders));
for (Customer customer:result3){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}
这里总共列举了四种排序方式:

1)直接创建Sort对象,适合对单一属性做排序
2)通过Sort.Order对象创建Sort对象,适合对单一属性做排序
3)通过属性的List集合创建Sort对象,适合对多个属性,采取同一种排序方式的排序
4)通过Sort.Order对象的List集合创建Sort对象,适合所有情况,比较容易设置排序方式

对应着我们的使用场景来进行选择创建Sort对象的方式。

注意,这里并没有列举所有的Sort使用方式,还有忽略大小写,使用JpaSort.unsafe、聚合函数等进行排序,查询的属性值是Entity的属性名,不是数据库的字段,要注意到!!
 
更多用法,请参考源码:http://www.icnws.com/wp-content/uploads/2017/06/Sort.java_.txt
 
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com 查看全部
通过上一节的学习,我们知道了如何用@Query注解来实现灵活的查询。在上一节的示例中,我也尝试给出简单的排序,通过JPQL语句以及原生SQL来实现的。这样的实现,虽然在一定程度上可以应用,但是灵活度不够,因此结合@Query注解,我们可以使用Sort来对结果进行排序。

1、在CustomerRepository内添加方法
    /**
* 一个参数,匹配两个字段
* @param name2
* @param sort 指定排序的参数,可以根据需要进行调整
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
*
*/
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name")
List<Customer> findByName4(@Param("name") String name2,Sort sort);
方法一如既往,是声明式的,只是在原有方法的基础上,加上Sort(org.springframework.data.domain.Sort)作为参数即可。
 
2、在CustomerController中测试
    /**
* @Query注解方式查询,
* 用@Param指定参数,匹配firstName和lastName
*/
@RequestMapping("/findByName")
public void findByName4(){
//按照ID倒序排列
System.out.println("直接创建sort对象,通过排序方法和属性名");
Sort sort = new Sort(Sort.Direction.DESC,"id");
List<Customer> result = repository.findByName4("Bauer",sort);
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
//按照ID倒序排列
System.out.println("通过Sort.Order对象创建sort对象");
Sort sortx = new Sort(new Sort.Order(Sort.Direction.DESC,"id"));
List<Customer> resultx = repository.findByName4("Bauer",sort);
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");

System.out.println("通过排序方法和属性List创建sort对象");
List<String> sortProperties = new ArrayList<>();
sortProperties.add("id");
sortProperties.add("firstName");
Sort sort2 = new Sort(Sort.Direction.DESC,sortProperties);
List<Customer> result2 = repository.findByName4("Bauer",sort2);
for (Customer customer:result2){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");

System.out.println("通过创建Sort.Order对象的集合创建sort对象");
List<Sort.Order> orders = new ArrayList<>();
orders.add(new Sort.Order(Sort.Direction.DESC,"id"));
orders.add(new Sort.Order(Sort.Direction.ASC,"firstName"));
List<Customer> result3 = repository.findByName4("Bauer",new Sort(orders));
for (Customer customer:result3){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

这里总共列举了四种排序方式:

1)直接创建Sort对象,适合对单一属性做排序
2)通过Sort.Order对象创建Sort对象,适合对单一属性做排序
3)通过属性的List集合创建Sort对象,适合对多个属性,采取同一种排序方式的排序
4)通过Sort.Order对象的List集合创建Sort对象,适合所有情况,比较容易设置排序方式


对应着我们的使用场景来进行选择创建Sort对象的方式。

注意,这里并没有列举所有的Sort使用方式,还有忽略大小写,使用JpaSort.unsafe、聚合函数等进行排序,查询的属性值是Entity的属性名,不是数据库的字段,要注意到!!
 
更多用法,请参考源码:http://www.icnws.com/wp-content/uploads/2017/06/Sort.java_.txt
 
 
 参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com

Spring Data JPA系列:使用@Query注解(Using @Query)

wayne 发表了文章 • 0 个评论 • 352 次浏览 • 2017-07-01 12:39 • 来自相关话题

经过几天的折腾,终于到了学习一个重量级的查询方式上,使用@Query注解,使用注解有两种方式,一种是JPQL的SQL语言方式,一种是原生SQL的语言,略有区别,后者我们更熟悉一些。话不多说,看代码。

1、在CustomerRepository里添加
/**
* 模糊匹配
* @param bauer
* @return
*/
@Query("select c from Customer c where c.firstName=?1")
Customer findByFirstName2(String bauer);

@Query("select c from Customer c where c.lastName=?1 order by c.id desc")
List<Customer> findByLastName2(String lastName);

/**
* 一个参数,匹配两个字段
* @param name2
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
*/
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name order by c.id desc")
List<Customer> findByName(@Param("name") String name2);

/**
* 一个参数,匹配两个字段
* @param name
* @return
* 这里的%只能放在占位的前面,后面不行
*/
@Query("select c from Customer c where c.firstName like %?1")
List<Customer> findByName2(@Param("name") String name);

/**
* 一个参数,匹配两个字段
* @param name
* @return
* 开启nativeQuery=true,在value里可以用原生SQL语句完成查询
*/
@Query(nativeQuery = true,value = "select * from Customer c where c.first_name like concat('%' ,?1,'%') ")
List<Customer> findByName3(@Param("name") String name);
2、在CustomerController内添加​
  /**
* @Query注解方式查询
* 查询FirstName为指定字符串
*/
@RequestMapping("/findByFirstName2")
public void findByFirstName2(){
Customer customer = repository.findByFirstName2("Bauer");
if(customer!=null){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

/**
* @Query注解方式查询
* 查询LastName为指定字符串
*/
@RequestMapping("/findByLastName2")
public void findByLastName2(){
List<Customer> result = repository.findByLastName2("Bauer");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

/**
* @Query注解方式查询,
* 用@Param指定参数,匹配firstName和lastName
*/
@RequestMapping("/findByName")
public void findByName(){
List<Customer> result = repository.findByName("Bauer");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

/**
* @Query注解方式查询,使用关键词like
* 用@Param指定参数,firstName的结尾为e的字符串
*/
@RequestMapping("/findByName2")
public void findByName2(){
List<Customer> result = repository.findByName2("e");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

/**
* @Query注解方式查询,模糊匹配关键字e
*/
@RequestMapping("/findByName3")
public void findByName3(){
List<Customer> result = repository.findByName3("e");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}可能看了上面的代码有些疑惑,这里做一下解释:
?加数字表示占位符,?1代表在方法参数里的第一个参数,区别于其他的index,这里从1开始

=:加上变量名,这里是与方法参数中有@Param的值匹配的,而不是与实际参数匹配的

JPQL的语法中,表名的位置对应Entity的名称,字段对应Entity的属性,详细语法见相关文档

要使用原生SQL需要在@Query注解中设置nativeQuery=true,然后value变更为原生SQL即可
 
 
参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com 查看全部
经过几天的折腾,终于到了学习一个重量级的查询方式上,使用@Query注解,使用注解有两种方式,一种是JPQL的SQL语言方式,一种是原生SQL的语言,略有区别,后者我们更熟悉一些。话不多说,看代码。

1、在CustomerRepository里添加
    /**
* 模糊匹配
* @param bauer
* @return
*/
@Query("select c from Customer c where c.firstName=?1")
Customer findByFirstName2(String bauer);

@Query("select c from Customer c where c.lastName=?1 order by c.id desc")
List<Customer> findByLastName2(String lastName);

/**
* 一个参数,匹配两个字段
* @param name2
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
*/
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name order by c.id desc")
List<Customer> findByName(@Param("name") String name2);

/**
* 一个参数,匹配两个字段
* @param name
* @return
* 这里的%只能放在占位的前面,后面不行
*/
@Query("select c from Customer c where c.firstName like %?1")
List<Customer> findByName2(@Param("name") String name);

/**
* 一个参数,匹配两个字段
* @param name
* @return
* 开启nativeQuery=true,在value里可以用原生SQL语句完成查询
*/
@Query(nativeQuery = true,value = "select * from Customer c where c.first_name like concat('%' ,?1,'%') ")
List<Customer> findByName3(@Param("name") String name);
2、在CustomerController内添加​
 
    /**
* @Query注解方式查询
* 查询FirstName为指定字符串
*/
@RequestMapping("/findByFirstName2")
public void findByFirstName2(){
Customer customer = repository.findByFirstName2("Bauer");
if(customer!=null){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

/**
* @Query注解方式查询
* 查询LastName为指定字符串
*/
@RequestMapping("/findByLastName2")
public void findByLastName2(){
List<Customer> result = repository.findByLastName2("Bauer");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

/**
* @Query注解方式查询,
* 用@Param指定参数,匹配firstName和lastName
*/
@RequestMapping("/findByName")
public void findByName(){
List<Customer> result = repository.findByName("Bauer");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

/**
* @Query注解方式查询,使用关键词like
* 用@Param指定参数,firstName的结尾为e的字符串
*/
@RequestMapping("/findByName2")
public void findByName2(){
List<Customer> result = repository.findByName2("e");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

/**
* @Query注解方式查询,模糊匹配关键字e
*/
@RequestMapping("/findByName3")
public void findByName3(){
List<Customer> result = repository.findByName3("e");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}
可能看了上面的代码有些疑惑,这里做一下解释:
?加数字表示占位符,?1代表在方法参数里的第一个参数,区别于其他的index,这里从1开始

=:加上变量名,这里是与方法参数中有@Param的值匹配的,而不是与实际参数匹配的

JPQL的语法中,表名的位置对应Entity的名称,字段对应Entity的属性,详细语法见相关文档

要使用原生SQL需要在@Query注解中设置nativeQuery=true,然后value变更为原生SQL即可
 
 
参考:
官方文档,https://docs.spring.io/spring- ... /html 
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com

Spring Data JPA系列:预定义查询(NamedQueries)

wayne 发表了文章 • 0 个评论 • 306 次浏览 • 2017-06-30 09:01 • 来自相关话题

前面讲了Spring-data-JPA的基本配置、继承的方法和创建查询,都比较简单,本节讲预定义查询(NamedQueries)。

预定义查询有两种,一种是通过XML配置或配置@NamedQuery,另一种是通过XML配置或配置@NamedNativeQuery,实现。对于配置XML的示例,这里就不做演示了,重点是通过Annotation实现的。
 
1、修改实体(Entity)

在@Entity下增加@NamedQuery定义,需要注意,这里的sql表达式里的表名要和当前的Entity一致,否则会找不到,报错!!!查询参数也要和实体进行对应起来,是firstName而不是first_name,切记!!

当然,有人可能会问会不会有复杂的表查询,这里不做探究,因为实际场景中这种破坏Entity的侵入式很不美感,也不方便,有更优雅的方式可选择,后面会提到。package com.example.demo.dto;
import org.hibernate.annotations.NamedQuery;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
@NamedQuery(name="Customer.findByFirstName",query = "select c from Customer c where c.firstName = ?1")
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
protected Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
}2、修改CustomerRepository

增加方法:Customer findByFirstName(String bauer);3、修改CustomerController

增加方法: /**
* 查询FirstName为指定用户昵称
*/
@RequestMapping("/findByFirstName")
public void findByFirstName(){
Customer customer = repository.findByFirstName("Bauer");
if(customer!=null){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}
 关于预定义查询(NamedQueries)的入门就到这里,如果想深入使用,需要深入的去摸索,挖坑填坑。下一节是使用@Query注解,实现查询,相对而言会更灵活一些。

参考:
官方文档,https://docs.spring.io/spring-data/jpa/docs/current/reference/html
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com 查看全部
前面讲了Spring-data-JPA的基本配置、继承的方法和创建查询,都比较简单,本节讲预定义查询(NamedQueries)。

预定义查询有两种,一种是通过XML配置或配置@NamedQuery,另一种是通过XML配置或配置@NamedNativeQuery,实现。对于配置XML的示例,这里就不做演示了,重点是通过Annotation实现的。
 
1、修改实体(Entity)

在@Entity下增加@NamedQuery定义,需要注意,这里的sql表达式里的表名要和当前的Entity一致,否则会找不到,报错!!!查询参数也要和实体进行对应起来,是firstName而不是first_name,切记!!

当然,有人可能会问会不会有复杂的表查询,这里不做探究,因为实际场景中这种破坏Entity的侵入式很不美感,也不方便,有更优雅的方式可选择,后面会提到。
package com.example.demo.dto;
import org.hibernate.annotations.NamedQuery;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
@NamedQuery(name="Customer.findByFirstName",query = "select c from Customer c where c.firstName = ?1")
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
protected Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
}
2、修改CustomerRepository

增加方法:
Customer findByFirstName(String bauer);
3、修改CustomerController

增加方法:
    /**
* 查询FirstName为指定用户昵称
*/
@RequestMapping("/findByFirstName")
public void findByFirstName(){
Customer customer = repository.findByFirstName("Bauer");
if(customer!=null){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}

 关于预定义查询(NamedQueries)的入门就到这里,如果想深入使用,需要深入的去摸索,挖坑填坑。下一节是使用@Query注解,实现查询,相对而言会更灵活一些。

参考:
官方文档,https://docs.spring.io/spring-data/jpa/docs/current/reference/html
DEMO,https://github.com/icnws/spring-data-jpa-demo
个人博客,http://www.icnws.com