SpringDataJpa
概述
什么是SpringDataJpa?
他是一个Spring继承的实现ORM的框架
它自己集成了Jpa,让我们对数据库的操作更加简单
搭建
注入依赖
在项目pom文件中加入
<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>aishell</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>
创建repository层(就是dao层)
定义接口
/**
* 继承JpaRepository接口,自动帮我们写增删改查,只需要将泛型定义好即可
*/
public interface EmployeeRepository extends JpaRepository<Employee,Long>, JpaSpecificationExecutor<Employee> {
}
创建domain层
配置实体类的时候我们将ID抽取出来,先创建好父类
@MappedSuperclass //告诉JPA这是一个父类
public class BaseDomain {
@Id
@GeneratedValue
protected Long id;
//getset方法
子类
@Entity
@Table(name = "employee")
public class Employee extends BaseDomain{
private String username;
private String password;
private String email;
private String headImage;
private Integer age;
private Long department_id;
//getset方法等
准备好db.properties文件
在resources文件夹下面准备db.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///aishell
jdbc.username=root
jdbc.password=123456
配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
">
<!-- 扫描Serivce -->
<context:component-scan base-package="com.ifueen.aishell.service"/>
<!-- datasource的配置 -->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- 配置连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置事务 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 配置对事务的支持 -->
<tx:annotation-driven></tx:annotation-driven>
<!-- 配置SpringDataJpa -->
<jpa:repositories base-package="com.ifueen.aishell.repository" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory">
</jpa:repositories>
</beans>
测试增删改查等操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class EmployeeRepositorTest {
@Autowired
private EmployeeRepository repository;
/**
* 查询所有
*/
@Test
public void findAll(){
List<Employee> list = repository.findAll();
list.forEach(employee -> System.out.println(employee));
}
/**
* 通过ID查询
*/
@Test
public void findById(){
Employee one = repository.findOne(1L);
System.out.println(one);
}
/**
* 保存
*/
@Test
public void save(){
Employee employee = new Employee();
employee.setUsername("韩梅梅");
employee.setPassword("454545");
employee.setAge(18);
repository.save(employee);
}
/**
* 通过ID删除
*/
@Test
public void delById(){
repository.delete(274L);
}
/**
* 更新
*/
@Test
public void update(){
Employee employee = repository.findOne(274L);
employee.setUsername("山泥若");
repository.save(employee);
}
/**
* 排序
*/
@Test
public void sort(){
Sort sort = new Sort(Sort.Direction.DESC,"age");
List<Employee> employees = repository.findAll(sort);
employees.forEach(employee -> System.out.println(employee));
}
通过规则操作数据库
必须遵守SpringDataJpa的规则命名,否则没有效果,具体规则请查询官方文档
//通过用户名查询
Employee findByUsername(String username);
//模糊查询,通过用户名
List<Employee> findByUsernameLike(String username);
//通过用户名和邮件的模糊查询
List<Employee> findByUsernameLikeAndEmailLike(String username,String email);
通过注解方式进行SQL查询
//通过注解的方式查找用户
@Query("select e from Employee e where e.username = ?1")
Employee query(String username);
//原生SQL的方法
@Query(nativeQuery = true,value = "select * from employee")
List<Employee> query02();
//通过注解实现模糊查询
@Query("select e from Employee e where e.username like ?1 and e.email like ?2")
List<Employee> query03(String username,String email);
JpaSpecificationExecutor
概述
JpaSpecificationExecutor(JPA规则执行者)是JPA2.0提供的Criteria API的使用封装,可以用于动态生成Query来满足我们业务中的各种复杂场景。
Spring Data JPA为我们提供了JpaSpecificationExecutor接口,只要简单实现toPredicate方法就可以实现复杂的查询
基本查询
/**
* 利用jpaSpecificationExecutor完成模糊查询
*/
@Test
public void jpaSpecificationExecutor(){
List<Employee> username = repository.findAll(new Specification<Employee>() {
/**
*
* @param root 根,可以看作是字段
* @param criteriaQuery 连接的,and or等..
* @param criteriaBuilder sql中的条件或者组合等..
* @return
*/
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path username = root.get("username");
Predicate predicate = criteriaBuilder.like(username, "%1%");
return predicate;
}
});
username.forEach(employee -> System.out.println(employee));
}
高级查询
/**
* 多个条件查询
* 通过username、email、和年龄来进行查询
*/
@Test
public void jpaSpecificationExecutor02(){
List<Employee> employees = repository.findAll(new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path username = root.get("username");
Predicate p1 = criteriaBuilder.like(username, "%1%");
Path email = root.get("email");
Predicate p2 = criteriaBuilder.like(email, "%2%");
Path age = root.get("age");
Predicate p3 = criteriaBuilder.gt(age, 18);
Predicate p4 = criteriaBuilder.and(p1, p2, p3);
return p4;
}
});
employees.forEach(employee -> System.out.println(employee));
}
/**
* 高级查询和分页
*/
@Test
public void jpaSpecificationExecutor03(){
PageRequest pageRequest = new PageRequest(0, 10);
Page<Employee> page = repository.findAll(new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path username = root.get("username");
Predicate predicate = criteriaBuilder.like(username, "%1%");
return predicate;
}
}, pageRequest);
page.forEach(employee -> System.out.println(employee));
}
/**
* 高级查询+分页+排序
* 其实就是在分页的对象里面加上一个排序即可
*/
@Test
public void jpaSpecificationExecutor04(){
Sort sort = new Sort(Sort.Direction.DESC, "age");
PageRequest pageRequest = new PageRequest(0, 10,sort);
Page<Employee> page = repository.findAll(new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path username = root.get("username");
Predicate predicate = criteriaBuilder.like(username, "%1%");
return predicate;
}
}, pageRequest);
page.forEach(employee -> System.out.println(employee));
}
Jpa-Spec
概述
这是一个由个人开发集成的拓展JpaSpecificationExecutor工具,可以较少代码的编写
并且在GitHub上面开源
简单查询
/**
* 使用jpa-spec进行模糊查询
*/
@Test
public void jpaSpec01(){
Specification<Employee> specification = Specifications.<Employee>and().like("username", "%1%").build();
List<Employee> employees = repository.findAll(specification);
employees.forEach(employee -> System.out.println(employee));
}
高级查询部分
/**
* 多个条件查询
*/
@Test
public void jpaSpec02(){
Specification<Employee> specification = Specifications.<Employee>and().like("username", "%1%")
.like("email", "%2%")
.gt("age", 18).build();
List<Employee> employees = repository.findAll(specification);
employees.forEach(employee -> System.out.println(employee));
}
/**
* 高级查询和分页
* 就在刚刚的基础上面加上一个分页对象即可
*/
@Test
public void jpaSpec03(){
Specification<Employee> specification = Specifications.<Employee>and().like("username", "%1%")
.like("email", "%2%")
.gt("age", 18).build();
PageRequest pageRequest = new PageRequest(0, 10);
Page<Employee> page = repository.findAll(specification, pageRequest);
page.forEach(employee -> System.out.println(employee));
}
抽取Query层
为了模拟真是项目中的情况(分页参数是从前台传到后台),并且为了以后的方法拓展和功能添加,我们抽取一个query类来进行集成
父类
package com.ifueen.query;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
public abstract class BaseQuery {
//所有的子类都必须实现这个抽象方法
public abstract Specification createSpec();
//创建排序的对象
public Sort createSort(){
if (StringUtils.isNotBlank(orderName)){
//通过三目运算符返回sort对象
Sort sort = new Sort(orderType ? Sort.Direction.DESC : Sort.Direction.ASC, orderName);
return sort;
}
return null;
}
//当前页数
private int currentPage = 1;
//每页显示记录数
private int pageSize = 10;
//排序 如果为true,变为升序,false为降序
private boolean orderType;
//排序的字段,如果这个字段为null就代表不排序
private String orderName;
public int getCurrentPage() {
//因为用户从1输入,然而JPA却是从0开始计算
return currentPage-1;
}
//getset方法
}
每个不同的子类
package com.ifueen.query;
import com.github.wenhao.jpa.Specifications;
import com.ifueen.aishell.domain.Employee;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
public class EmployeeQuery extends BaseQuery{
//用户名
private String username;
//邮箱
private String email;
//年龄
private Integer age;
//getset方法
//覆写创建Specifications对象的方法
@Override
public Specification createSpec() {
Specification<Employee> specification = Specifications.<Employee>and().like(StringUtils.isNoneBlank(username), "username", "%" + username + "%")
.like(StringUtils.isNoneBlank(email), "email", "%" + email + "%")
.gt(age != null, "age", age).build();
return specification;
}
}
测试
/**
* 测试query
*/
@Test
public void jpaSpec04(){
EmployeeQuery employeeQuery = new EmployeeQuery();
employeeQuery.setOrderName("age");
employeeQuery.setOrderType(true);
Sort sort = employeeQuery.createSort();
PageRequest pageRequest = new PageRequest(employeeQuery.getCurrentPage(), employeeQuery.getPageSize(), sort);
Specification spec = employeeQuery.createSpec();
Page<Employee> page = repository.findAll(spec, pageRequest);
page.forEach(employee -> System.out.println(employee));
}