很早之前就想写关于JPA的文章了,但是基于到时很赖,写不下来,那今天我们就慢慢的开始写,写多少就多少吧!
什么是JPA
JPA是Java Persistence API的简称,JPA是一个基于O/R映射的标准规范,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。而JPA是需要Provider来实现其功能的,Hibernate就是JPA Provider中很强的一个。
Hibernate 从3.2开始,就开始兼容JPA。Hibernate3.2获得了Sun TCK的JPA(Java Persistence API) 兼容认证,在Hibernate有自由、持久、游离三种,JPA里有new,managed,detached,removed,明眼人一看就知道,这些状态都是一一对应的。再如flush方法,都是对应的,而其他的再如说Query query = manager.createQuery(sql),它在Hibernate里写法上是session,而在JPA中变成了manager,所以从Hibernate到JPA的代价应该是非常小的同样,JDO,也开始兼容JPA。在ORM的领域中,看来JPA已经是王道,规范就是规范。在各大厂商的支持下,JPA的使用开始变得广泛。
使用JPA
创建实体类NBArticle
那么我们可以这样定义一个Dao层
public interface ArticleRepository extends JpaRepository<NBArticle, Long>, JpaSpecificationExecutor<NBArticle> {
}
就短短的这几行代码,我们就有了以下这几个功能
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll(); //查询所有
List<T> findAll(Sort var1);
List<T> findAllById(Iterable<ID> var1);
<S extends T> List<S> saveAll(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
看是不是很方便啊,如果以上这些方法还不能满足你的要求,那么我们可以自己定义方法啊
查询总数
public interface ArticleRepository extends JpaRepository<NBArticle, Long>, JpaSpecificationExecutor<NBArticle> {
/**
* 查询满足UrlSequence相关的总数
*
* @param urlSequence
* @return
*/
long countByUrlSequence(String urlSequence);
/**
* 根据满足UrlSequence和draft变量计算文章数量
*
* @param draft
* @return
*/
long countByUrlSequenceAndDraft(String urlSequence,boolean draft);
}
查找对象
//根据文章自定义链接查找文章对象
List<NBArticle> findNBArticleByUrlSequence(String urlSeq);
//通过自定义链接like查询
List<NBArticle> findNBArticleByUrlSequenceLike(String urlSeq);
//
//通过自定义链接和dreaft查询
List<NBArticle> findNBArticleByUrlSequenceAndDraft(String urlSeq,boolean draft);
关键字
从上面的代码我们可以看出我们可以用一些关键字代表一些语句,表格如下
关键字 | 示例 | JPQL同等功能 |
---|---|---|
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 ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection age) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
限制数量查询(top或者frist)
那么有时我们只要查询几条,如我只要查询5条文章
//查询满足UrlSequence的5条记录(First)
List<NBArticle> findFirst5ByUrlSequence(String urlSeq);
//top
List<NBArticle> findTop5ByUrlSequence(String urlSeq);
Sql语句操作
有人说,我习惯用SQL查询,当然,这里也可以用sql,用到注解@Query,在这个注解里有个参数nativeQuery,为true是,表示是原生的操作表,如果为false,则操作的是实体类。表名是nb_article
查询
/**
* 查找出最大的 top 值
*
* @return
*/
@Query(nativeQuery = true, value = "SELECT MAX(top)FROM nb_article ")
int findMaxTop();
/**
* 查找相似的文章 参数是一一对应的
*
* @param cateId
* @param limit
* @return
*/
@Query(nativeQuery = true, value = "SELECT * FROM nb_article WHERE cate_id = ?1 ORDER BY rand() LIMIT ?2")
List<NBArticle> findSimilarArticles(long cateId, int limit);
update
更新时,我们还要在加个注解,就是@Modifying 我们还可以加个事务的注解 @Transactional(rollbackOn = Exception.class)
/**
* 更新 appreciable 状态 默认不是原生的
*
* @param appreciable
* @param id
* @return
*/
@Modifying
@Query("update NBArticle a set a.appreciable = ?1 where a.id = ?2")
@Transactional(rollbackOn = Exception.class)
int updateAppreciableById(boolean appreciable, long id);
//原生的
/**
* 更新文章点赞数
*
* @param articleId
* @return
* @throws Exception
*/
@Query(nativeQuery = true, value = "UPDATE nb_article SET approve_cnt = approve_cnt + 1 WHERE id = ?1")
@Modifying
@Transactional(rollbackOn = Exception.class)
int updateApproveCntById(long articleId);
ExampleMatcher实例查询
这个是个好东西,对应那些对sql语句不好的程序员,可以用这个查询
实体对象:在ORM框架中与Table对应的域对象,一个对象代表数据库表中的一条记录,如上例中User对象,对应user表。在构建查询条件时,一个实体对象代表的是查询条件中的“数值”部分。如:要查询姓“X”的客户,实体对象只需要存储条件值“X”。
匹配器:ExampleMatcher对象,它是匹配“实体对象”的,表示了如何使用“实体对象”中的“值”进行查询,它代表的是“查询方式”,解释了如何去查的问题。如:要查询姓“X”的客户,即姓名以“X”开头的客户,该对象就表示了“以某某开头的”这个查询方式,如上例中:withMatcher(“userName”, GenericPropertyMatchers.startsWith())
实例:即Example对象,代表的是完整的查询条件。由实体对象(查询条件值)和匹配器(查询方式)共同创建。最终根据实例来findAll即可
NBArticle prob = NBArticle.builder().textContent(searchStr)
.title(searchStr).build(); //创建查询实体
if (articleQueryBO.getCateId() != null) {
prob.setCateId(articleQueryBO.getCateId());
}
prob.setDraft(false); //查询实体 就是我们要查询的条件
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("title", ExampleMatcher.GenericPropertyMatcher::contains)
.withMatcher("textContent", ExampleMatcher.GenericPropertyMatcher::contains)//包含,就是like
.withIgnorePaths("post", "modify", "view", "approveCnt", "commented", "mdContent", "appreciable", "top", "draft")//忽略属性列表,忽略的属性不参与查询过滤。
.withIgnoreNullValues(); //匹配器
Example<NBArticle> articleExample = Example.of(prob, matcher);//实例
List<NBArticle> pages = articleRepository.findAll(articleExample);
//Page page = articleRepository.findAll(articleExample, pageable);分页查询
分页查询
在分页查询中,我们要extends接口 JpaSpecificationExecutor,这样我们就有以下的功能
public interface JpaSpecificationExecutor<T> {
Optional<T> findOne(@Nullable Specification<T> var1);
List<T> findAll(@Nullable Specification<T> var1);
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
List<T> findAll(@Nullable Specification<T> var1, Sort var2);
long count(@Nullable Specification<T> var1);
}
page对象
public interface Page<T> extends Slice<T> {
static <T> Page<T> empty() {
return empty(Pageable.unpaged());
}
static <T> Page<T> empty(Pageable pageable) {
return new PageImpl(Collections.emptyList(), pageable, 0L);
}
int getTotalPages(); //总页数
long getTotalElements(); //元素的总数
<U> Page<U> map(Function super T, ? extends U> var1);
}
还有其他方法
/**
* 分页信息 第一页从1开始
*
*/
public class Page<T> implements Serializable, Iterable<T> {
protected List<T> result;
protected int pageSize=10;
protected int pageNumber=1;
/**
* 总记录数
*/
protected long totalCount = 0;
protected boolean isPagination = true;
private int pageNum;//总页数
private int startIndex;
private int limit;
public int getPageNum() {
int totalPageNum = (int) ((totalCount % pageSize == 0) ? totalCount / pageSize : totalCount / pageSize + 1);
return totalPageNum;
}
public Page() {
}
public Page(int pageNumber, int pageSize, long totalCount) {
this(pageNumber, pageSize, totalCount, new ArrayList(0));
}
public Page(int pageNumber, int pageSize, long totalCount, List<T> result) {
if (pageSize <= 0)
throw new IllegalArgumentException("[pageSize] must great than zero");
this.pageSize = pageSize;
this.pageNumber = PageUtils.computePageNumber(pageNumber, pageSize, totalCount);
this.totalCount = totalCount;
setResult(result);
}
public void setResult(List<T> elements) {
if (elements == null)
throw new IllegalArgumentException("'result' must be not null");
this.result = elements;
}
/**
* 当前页包含的数据
*
* @return 当前页数据源
*/
public List<T> getResult() {
return result;
}
/**
* 是否是首页(第一页),第一页页码为1
*
* @return 首页标识
*/
public boolean isFirstPage() {
return getThisPageNumber() == 1;
}
/**
* 是否是最后一页
*
* @return 末页标识
*/
public boolean isLastPage() {
return getThisPageNumber() >= getLastPageNumber();
}
/**
* 是否有下一页
*
* @return 下一页标识
*/
public boolean isHasNextPage() {
return getLastPageNumber() > getThisPageNumber();
}
/**
* 是否有上一页
*
* @return 上一页标识
*/
public boolean isHasPreviousPage() {
return getThisPageNumber() > 1;
}
/**
* 获取最后一页页码,也就是总页数
*
* @return 最后一页页码
*/
public int getLastPageNumber() {
return PageUtils.computeLastPageNumber(totalCount, pageSize);
}
/**
* 总的数据条目数量,0表示没有数据
*
* @return 总数量
*/
public long getTotalCount() {
return totalCount;
}
public void setTotalCount(long totalCount) {
this.totalCount = totalCount;
}
/**
* 获取当前页的首条数据的行编码
*
* @return 当前页的首条数据的行编码
*/
public int getThisPageFirstElementNumber() {
return (getThisPageNumber() - 1) * getPageSize() + 1;
}
/**
* 获取当前页的末条数据的行编码
*
* @return 当前页的末条数据的行编码
*/
public long getThisPageLastElementNumber() {
int fullPage = getThisPageFirstElementNumber() + getPageSize() - 1;
return getTotalCount() < fullPage ? getTotalCount() : fullPage;
}
/**
* 获取下一页编码
*
* @return 下一页编码
*/
public int getNextPageNumber() {
return getThisPageNumber() + 1;
}
/**
* 获取上一页编码
*
* @return 上一页编码
*/
public int getPreviousPageNumber() {
return getThisPageNumber() - 1;
}
/**
* 每一页显示的条目数
*
* @return 每一页显示的条目数
*/
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
/**
* 当前页的页码
*
* @return 当前页的页码
*/
public int getThisPageNumber() {
return pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
/**
* 得到用于多页跳转的页码
*
* @return
*/
public List<Integer> getLinkPageNumbers() {
return PageUtils.generateLinkPageNumbers(getThisPageNumber(), getLastPageNumber(), 10);
}
/**
* 得到数据库的第一条记录号
*
* @return
*/
public int getFirstResult() {
return PageUtils.getFirstResult(pageNumber, pageSize);
}
public Iterator<T> iterator() {
return (Iterator<T>) (result == null ? Collections.emptyList().iterator() : result.iterator());
}
public boolean isPagination() {
return isPagination;
}
public void setPagination(boolean isPagination) {
this.isPagination = isPagination;
}
}
排序(Sort类)
Sort sort=new Sort(Direction.ASC,”id”);
其中第一个参数表示是降序还是升序(此处表示升序),第二个参数表示你要按你的实体类的,不是数据库的中的那个变量进行排序。如果要多个排序呢
//多个排序,用Map,再创建对象
Map<String, String> orders = new HashMap<>(2);
orders.put("top", "desc");
orders.put("post", "desc");
Sort sort = getJpaSortWithOther(pagination, orders);
有了这个类,我们可以放在分页中查询,也可以放在普通的查询:
//根据文章自定义链接查找文章对象
List<NBArticle> findNBArticleByUrlSequence(String urlSeq,Sort sort);
配置文件(pom.xml)
说了那么多,那我们怎么配置jpa呢,在SpringBoot中,我们引用下面的文件,其他配置SpringBoot会帮我们自动配置
org.springframework.boot
spring-boot-starter-data-jpa
配置文件(yaml)
#数据访问配置
jpa:
database: mysql
hibernate:
ddl-auto: update
show-sql: true
database-platform: me.wuwenbin.noteblogv4.config.configuration.LocalMySQL57InnoDBDialect
jackson:
date-format: yyyy-MM-dd HH:mm:ss
#最大上传文件大小
servlet:
multipart:
max-file-size: 20MB
cache:
#使用默认的ConcurrentMap
type: simple
#程序启动时创建的缓存名称
cache-names: paramCache,authCache
原创来源:滴一盘技术