Spring jpa
工欲善其事,必先利其器。像java这种多维度、高复杂度的东西,必须要有框架来支撑。
spring jpa 介绍
它是JPA规范的再次封装抽象,底层还是使用了Hibernate的JPA技术实现,引用JPQL的查询语句 ,是属于Spring的生成体系中的一部分。
SpringDataJpa使用起来比较方便,加快了开发的效果,使开发人员不需要关心和配置更多的东西。
SpringDataJpa上手简单,开发效率高,对对象的支持非常好,还十分的灵活。
Spring Data项目是从2010年开发发展起来的,是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。 Spring Data 包含多个子项目:
Commons - 提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化
JPA - 简化创建 JPA 数据访问层和跨存储的持久层功能
Hadoop - 基于 Spring 的 Hadoop 作业配置和一个 POJO 编程模型的 MapReduce 作业
Key-Value - 集成了 Redis 和 Riak ,提供多个常用场景下的简单封装
Document - 集成文档数据库:CouchDB 和 MongoDB 并提供基本的配置映射和资料库支持
Graph - 集成 Neo4j 提供强大的基于 POJO 的编程模型
Graph Roo AddOn - Roo support for Neo4j
JDBC Extensions - 支持 Oracle RAD、高级队列和高级数据类型
Mapping - 基于 Grails 的提供对象映射框架,支持不同的数据库
Examples - 示例程序、文档和图数据库
Guidance - 高级文档
springdate的结构
Spring Data JPA的七个Repository接口:
Repository(org.springframework.data.repository.Repository)
CrudRepository(org.springframework.data.repository.CrudRepository)
PagingAndSortingRepository(org.springframework.data.repository.PagingAndSortingRepository)
JpaRepository (org.springframework.data.jpa.repository.JpaRepository)
QueryByExampleExecutor(org.springframework.data.repository.query.QueryByExampleExecutor)
JpaSpecificationExecutor (org.springframework.data.jpa.repository.JpaSpecificationExecutor)
QueryDslPredicateExecutor (org.springframework.data.querydsl.QueryDslPredicateExecutor)
两大Repository实现类:
SimpleJpaRepository(org.springframework.data.jpa.repository.support.SimpleJpaRepository)
QueryDslJpaRepository(org.springframework.data.jpa.repository.support.QueryDslJpaRepository)
结构图(通过Intellij Idea,打开SimpleJpaRepository.java,单击鼠标右键show diagrams,就可以用图表的式打开与查询类的关系层次图):
CrudRepository:完成所有基本的增删改查的方法
package org.springframework.data.repository;
import java.io.Serializable;
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> save(Iterable<S> entities);
T findOne(ID id);
boolean exists(ID id);
Iterable<T> findAll();
Iterable<T> findAll(Iterable<ID> ids);
long count();
void delete(ID id);
void delete(T entity);
void delete(Iterable<? extends T> entities);
void deleteAll();
}
PagingAndSortingRepository:完成分页与排序功能
该接口继承了CrudRepository接口,提供了两个方法,实现了分页和排序的功能了。
package org.springframework.data.repository;
import java.io.Serializable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
JpaRepository:
该接口继承了PagingAndSortingRepository接口。
package org.springframework.data.jpa.repository;
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* JPA specific extension of {@link org.springframework.data.repository.Repository}.
*
* @author Oliver Gierke
*/
@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAll(Iterable<ID> ids);
<S extends T> List<S> save(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
}
注意与说明:
1.几个查询、及批量保存方法,和 CrudRepository 接口相比,返回的是 List,使用起来更方便。
2.增加了 InBatch 删除, 实际执行时,后台生成一条sql语句,效率更高些。相比较而言,CrudRepository 接口的删除方法,都是一条一条删除的,即便是 deleteAll 也是一条一条删除的。
3.增加了 getOne() 方法,切记,该方法返回的是对象引用,当查询的对象不存在时,它的值不是Null。
根据条件进行查询
按照规范创建查询方法,一般按照java驼峰式书写规范加一些特定关键字,例如我们想通过员工的名来获取到对应的员工的对象。
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
//根据名称模糊查询
List<Employee> findByUsernameLike(String username);
//根据名称进行查询根据条件进行查询
List<Employee> findByUsername(String username);
}
这种方案看起来有意思,但是功能它都只针对简单的单表查询(而且如果条件太多,这个名称还是比较麻烦),如果遇到复杂的查询,还有方案就显得力不从心了。
此外还可以使用@Query注解查询
在Respository方法中一定要按照查询方法的命名规则,其实是比较麻烦的如果我们想不遵循 查询方法的命名规则,还可以使用@Query的方法进行查询。
只需要将@Query定义在Respository的方法之上即可。
案例一:拿到一个员工(它的Id是所有员工最大的)
@Query("select o from cn.itsource.pss.domain.Employee o where o.id=(select max(id) from cn.itsource.pss.domain.Employee e)")
Employee query01();
案例二:根据用户名和密码拿到相应的员工
根据参数顺序
@Query("select o from cn.itsource.pss.domain.Employee o where o.username like ?1 and o.email like ?2")
List<Employee> query02(String username,String email);
根据参数名称
@Query("select o from cn.itsource.pss.domain.Employee o where o.username like :username and o.email like :email")
List<Employee> query03(@Param("username") String username,@Param("email") String email);
案例三:查询总条数(原生sql查询)
@Query(value="SELECT COUNT(*) FROM employee",nativeQuery = true)
Long queryCount();
Query的注解注入非常灵活,在开发中也经常使用到(只是写JPQL/SQL有点麻烦)。
JpaSpecificationExecutor的认识
JpaSpecificationExecutor(JPA规则执行者)是JPA2.0提供的Criteria API的使用封装,可以用于动态生成Query来满足我们业务中的各种复杂场景。
Spring Data JPA为我们提供了JpaSpecificationExecutor接口,只要简单实现toPredicate方法就可以实现复杂的查询。`
咱们先来看一下这个接口的方法(大家注意一下,所以查询都要求传入一个Specification对象):
SimpleJpaRepository也实现了这个接口
package org.springframework.data.jpa.repository;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
public interface JpaSpecificationExecutor<T> {
T findOne(Specification<T> spec);
List<T> findAll(Specification<T> spec);
Page<T> findAll(Specification<T> spec, Pageable pageable);
List<T> findAll(Specification<T> spec, Sort sort);
long count(Specification<T> spec);
}
我们要使用这个接口中的方法,首先让我们的接口也去继承这个接口
单个条件的查询
@Test
public void testFind() {
/**
*官方解释:
* Root<T> root:代表了可以查询和操作的实体对象的根,
* 可以通过它的 Path<Y> get(String attributeName); 这个方法拿到我们要操作的字段
* 注意:只可以拿到对应的T的字段(Employee)
* CriteriaQuery<?> query:代表一个specific的顶层查询对象
* 包含查询的各个部分,比如select,from,where,group by ,order by 等
* 简单理解 就是它提供 了查询ROOT的方法(where,select,having)
* CriteriaBuilder cb:用来构建CriteriaQuery的构建器对象(相当于条件或者说条件组合)
* 构造好后以Predicate的形式返回
*/
/**
* 非官方理解:
* 查询的时候就需要给一个标准(规范)
* -》 根据规范(这个规范我们可以先简单理解为查询的条件)进行查询
*
* Root:查询哪个表(定位到表和字段-> 用于拿到表中的字段)
* 可以查询和操作的实体的根
* Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似
* Root<Employee> 相当于 from Employee
* Root<Product> 相当于 from Product
* CriteriaQuery:查询哪些字段,排序是什么(主要是把多个查询的条件连系起来)
* CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式
* 主要判断关系(和这个字段是相等,大于,小于like等)
* Predicate(Expression):单独每一条查询条件的详细描述 整个 where xxx=xx and yyy=yy ...
*/
List<Employee> emps = employeeRepository.findAll(
new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path path = root.get("username");//拿到要做查询的字段
Predicate p = cb.like(path, "%1%");//like代表做模糊查询,后面就是它的条件值
return p;
}
}
);
for (Employee emp : emps) {
System.out.println(emp);
}
}
4.2.多个条件查询
@Test
public void testFind02() {
Specification spec = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
//加上第一个条件: username like '%1%'
Path path1 = root.get("username");
Predicate p1 = cb.like(path1, "%1%");
//加上第二个条件: email like '%2%'
Path path2 = root.get("email");
Predicate p2 = cb.like(path2,"%2%");
//加上第二个条件: age < 20
Path path3 = root.get("age");
Predicate p3 = cb.lt(path3, 20);
//下面是加上or的条件的方案
//Predicate p3 = cb.or(p1,p2);
//把两个查询条件放到query对象中去(条件使用where)
CriteriaQuery where = query.where(p1, p2, p3);
//返回查询条件
return where.getRestriction();
}
};
List<Employee> emps = employeeRepository.findAll(spec);
for (Employee emp : emps) {
System.out.println(emp);
}
}
4.3.条件查询+分页排序
@Test
public void testFind03() {
Specification spec = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
//加上条件: username like '%1%'
Path path1 = root.get("username");
Predicate p1 = cb.and(cb.like(path1, "%1%"));
//把两个查询条件放到query对象中去(条件使用where)
CriteriaQuery where = query.where(p1);
//返回查询条件
return where.getRestriction();
}
};
//排序 :第一个参数是排序的规则(DESC/ASC) 后面参数是排序的字符
Sort sort = new Sort(Sort.Direction.DESC,"username");
Pageable pageable = new PageRequest(0, 10,sort);
Page<Employee> page = employeeRepository.findAll(spec, pageable);
for (Employee emp : page) {
System.out.println(emp);
}
}
JpaSpecificationExecutor咱们已经学完了,功能也是非常强大的,可以让我们不写一句SQL即可完成相应的查询功能。在外面也很不少公司使用这种方法,然后直接可以进行功能的抽取。
不过这个方案在设计上理解不是很容易,而且代码还比较臃肿烦杂(如果写得多习惯了还好)。
jpa-spec插件
这是一个对于咱们刚才的动态生成Query功能的一个封装版,如果我们使用这个插件,在完成查询与分页的时候功能会简单不少。
基于Spring Data Jpa的动态查询库 https://github.com/wenhao/jpa-spec
5.1.功能介绍
1.兼容Spring Data Jpa 和JPA2.1接口。
2.Equal/NotEqual/Like/NotLike/In/NotIn支持可变参数, Equal/NotEqual 支持空(Null)值。
3.每个条件支持关联查询。
4.支持自定义条件查询。
5.条件构建器。
6.支持分页和排序。
5.2.在Maven中引入相应的包
pom.xml(咱们项目中已经引入)
<!-- jpa的SpecificationSpecification功能封装 -->
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
<!-- 把所有依赖都过滤 -->
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
多个条件查询
@Test
public void testSpecFind02() {
Specification<Employee> spec = Specifications.<Employee>and().like("username", "%1%")
.like("email","%2%").lt("age", 20).build();
List<Employee> emps = employeeRepository.findAll(spec);
for (Employee emp : emps) {
System.out.println(emp);
}
}
5.3.3.条件+排序分页功能
//条件集成+分页
@Test
public void testSpecFind03() {
Specification<Employee> spec = Specifications.<Employee>and().like("username", "%1%").build();
//排序 :第一个参数是排序的规则(DESC/ASC) 后面参数是排序的字符
Sort sort = new Sort(Sort.Direction.DESC,"username");
Pageable pageable = new PageRequest(0, 10,sort);
Page<Employee> page = employeeRepository.findAll(spec, pageable);
for (Employee emp : page) {
System.out.println(emp);
}
}
学习spring jpa需要导入的jar包
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.springframework.version>4.2.5.RELEASE</org.springframework.version>
<org.hibernate.version>4.3.8.Final</org.hibernate.version>
<spring-data-jpa.version>1.9.0.RELEASE</spring-data-jpa.version>
<com.fasterxml.jackson.version>2.5.0</com.fasterxml.jackson.version>
<org.slf4j.version>1.6.1</org.slf4j.version>
</properties>
<dependencies>
<!-- Spring的支持包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<!-- 引入web前端的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- SpringMCV上传需要用到io包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 文件上传用到的包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<!-- SpringMVC的json支持包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<!-- hibernate的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${org.hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${org.hibernate.version}</version>
</dependency>
<!-- SpringData的支持包 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data-jpa.version}</version>
</dependency>
<!-- SpringData的擴展包 -->
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
<!-- 把所有的依賴都去掉 -->
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- 測試包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- 这个scope 只能作用在编译和测试时,同时没有传递性。表示在运行的时候不添加此jar文件 -->
<scope>provided</scope>
</dependency>
<!-- 日志文件 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<!-- 代码生成器模版技术 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.6</version>
</dependency>
<!-- shiro的支持包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- poi支持的jar包 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.11</version>
</dependency>
<!-- 图片压缩功能 -->
<!-- 缩略图 -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.6</version>
</dependency>
<!-- 定时调度 -->
<dependency>
<groupId>quartz</groupId>
<artifactId>quartz</artifactId>
<version>1.5.2</version>
</dependency>
<!-- 邮件支持 -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
<build>
<finalName>yxb</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.15.v20140411</version>
<configuration>
<stopPort>9966</stopPort>
<stopKey>foo</stopKey>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
</configuration>
</plugin>
</plugins>
</build>
</project>