Spring Data Jpa是 dao层的一个框架,简化数据库开发。作用和Mybatis框架一样,但是使用方式和底层机制有所不同。最大的差别就是,使用Spring Data Jpa开发,很多场景不需要写SQL。
官方网站:https://spring.io/projects/spring-data-jpa
Spring Data JPA 是 Spring 基于JPA 规范的基础上封装的⼀套 JPA 应⽤框架,可使开发者⽤极简的
代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常⽤功能!
学习并使⽤Spring Data JPA 可以极⼤提⾼开发效率。
Spring Data JPA 极⼤简化了数据访问层代码。
如何简化呢?使⽤了Spring Data JPA,我们Dao层中只需要写接⼝,不需要写实现类,就⾃动具有
了增删改查、分⻚查询等⽅法。
使用Spring Data jpa搭建入门案例,完成对mysql中数据表的简单增删改查、排序、分页等基础操作。
maven + spring + mysql+hibernate + spring data jpa
数据库建表sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_resume
-- ----------------------------
DROP TABLE IF EXISTS `tb_resume`;
CREATE TABLE `tb_resume` (
ìd` bigint(20) NOT NULL AUTO_INCREMENT,
àddress` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
PRIMARY KEY (ìd`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tb_resume
-- ----------------------------
BEGIN;
INSERT INTO `tb_resume` VALUES (1, '北京', '张三', '131000000');
INSERT INTO `tb_resume` VALUES (2, '上海', '李四', '151000000');
INSERT INTO `tb_resume` VALUES (3, '⼴州', '王五', '153000000');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.lagou.edugroupId>
<artifactId>spring-data-jpaartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-jpaartifactId>
<version>2.1.8.RELEASEversion>
dependency>
<dependency>
<groupId>javax.elgroupId>
<artifactId>javax.el-apiartifactId>
<version>3.0.1-b04version>
dependency>
<dependency>
<groupId>org.glassfish.webgroupId>
<artifactId>javax.elartifactId>
<version>2.2.6version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.8.13version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-coreartifactId>
<version>5.4.0.Finalversion>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-entitymanagerartifactId>
<version>5.4.0.Finalversion>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>5.4.0.Finalversion>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.46version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.21version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.1.12.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>11source>
<target>11target>
<encoding>UTF-8encoding>
configuration>
plugin>
plugins>
build>
project>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa
https://www.springframework.org/schema/data/jpa/spring-jpa.xsd
">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.lagou.edu.pojo"/>
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider">bean>
property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">bean>
property>
<property name="jpaVendorAdapter" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false"/>
<property name="database" value="MYSQL"/>
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
<property name="showSql" value="true"/>
bean>
property>
bean>
<jpa:repositories base-package="com.lagou.edu.dao" entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
bean>
<context:component-scan base-package="com.lagou.edu"/>
beans>
/**
* 简历实体类(在类中要使用注解建立实体类和数据表之间的映射关系以及属性和字段的映射关系)
* 1、实体类和数据表映射关系
* @Entity
* @Table
* 2、实体类属性和表字段的映射关系
* @Id 标识主键
* @GeneratedValue 标识主键的生成策略
* @Column 建立属性和字段映射
*/
@Entity
@Table(name = "tb_resume")
public class Resume {
@Id
/**
* 生成策略经常使用的两种:
* GenerationType.IDENTITY:依赖数据库中主键自增功能 Mysql
* GenerationType.SEQUENCE:依靠序列来产生主键 Oracle
*/
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "address")
private String address;
@Column(name = "phone")
private String phone;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Resume{" +
"id=" + id +
", name='" + name + '\'' +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ResumeDao extends JpaRepository<Resume,Long>, JpaSpecificationExecutor<Resume> {
}
import com.lagou.edu.pojo.Resume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.persistence.criteria.*;
import java.util.List;
import java.util.Optional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class ResumeDaoTest {
// 要测试IOC哪个对象注入即可
@Autowired
private ResumeDao resumeDao;
}
直接使用resumeDao自带的方法就可实现基本的查询,返回Optional对象
@Test
public void testFindById(){
// 早期的版本 dao.findOne(id);
/* 方法打印的SQL
select resume0_.id as id1_0_0_,
resume0_.address as address2_0_0_, resume0_.name as name3_0_0_,
resume0_.phone as phone4_0_0_ from tb_resume resume0_ where resume0_.id=?
*/
Optional<Resume> optional = resumeDao.findById(1l);
Resume resume = optional.get();
System.out.println(resume);
}
org.springframework.data.domain.Example <目标对象>
@Test
public void testFindOne(){
Resume resume = new Resume();
resume.setId(1l);
resume.setName("张三");
Example<Resume> example = Example.of(resume);
Optional<Resume> one = resumeDao.findOne(example);
Resume resume1 = one.get();
System.out.println(resume1);
}
新增和更新都使用save方法,通过传入的对象的主键有无来区分,没有主键信息那就是新增,有主键信息就是更新。
@Test
public void testSave(){
// 新增和更新都使用save方法,通过传入的对象的主键有无来区分,没有主键信息那就是新增,有主键信息就是更新
Resume resume = new Resume();
resume.setId(5l);
resume.setName("赵六六");
resume.setAddress("成都");
resume.setPhone("132000000");
Resume save = resumeDao.save(resume);
System.out.println(save);
}
@Test
public void testDelete(){
resumeDao.deleteById(5l);
}
@Test
public void testFindAll(){
List<Resume> list = resumeDao.findAll();
for (int i = 0; i < list.size(); i++) {
Resume resume = list.get(i);
System.out.println(resume);
}
}
org.springframework.data.domain.Sort
构建Sort对象后,传入Sort对象进行查询,即可对查询结果进行排序
@Test
public void testSort(){
Sort sort = new Sort(Sort.Direction.DESC,"id");
List<Resume> list = resumeDao.findAll(sort);
for (int i = 0; i < list.size(); i++) {
Resume resume = list.get(i);
System.out.println(resume);
}
}
@Test
public void testPage(){
/**
* 第一个参数:当前查询的页数,从0开始
* 第二个参数:每页查询的数量
*/
Pageable pageable = PageRequest.of(0,2);
//Pageable pageable = new PageRequest(0,2);
Page<Resume> all = resumeDao.findAll(pageable);
System.out.println(all);
}
在ResumeDao.java接口中定义一个方法,使用注解完成jpql的编写
@Query("from Resume where id=?1 and name=?2")
public List<Resume> findByJpql(Long id,String name);
测试调用
@Test
public void testJpql(){
List<Resume> list = resumeDao.findByJpql(1l, "张三");
for (int i = 0; i < list.size(); i++) {
Resume resume = list.get(i);
System.out.println(resume);
}
}
原生SQL查询,也是在dao接口的方法中使用注解编写sql,需要设置注解 nativeQuery = true,该属性默认为false
@Query(value = "select * from tb_resume where name like ?1 and address like ?2",nativeQuery = true)
public List<Resume> findBySql(String name,String address);
@Test
public void testSql(){
List<Resume> list = resumeDao.findBySql("李%", "上海%");
for (int i = 0; i < list.size(); i++) {
Resume resume = list.get(i);
System.out.println(resume);
}
}
/**
* 方法命名规则查询
* 按照name模糊查询(like)
* 方法名以findBy开头
* -属性名(首字母大写)
* -查询方式(模糊查询、等价查询),如果不写查询方式,默认等价查询
*/
public List<Resume> findByNameLikeAndAddress(String name,String address);
@Test
public void testMethodName(){
List<Resume> list = resumeDao.findByNameLikeAndAddress("李%","上海");
for (int i = 0; i < list.size(); i++) {
Resume resume = list.get(i);
System.out.println(resume);
}
}
toPredicate(Root root, CriteriaQuery> criteriaQuery, CriteriaBuilder criteriaBuilder)
方法。需要重写该方法来定义查询的条件。
单个条件查询用例
@Test
public void testSpecfication(){
/**
* 动态条件封装
* 匿名内部类
*
* toPredicate:动态组装查询条件
*
* 借助于两个参数完成条件拼装,,, select * from tb_resume where name='张三'
* Root: 获取需要查询的对象属性
* CriteriaBuilder:构建查询条件,内部封装了很多查询条件(模糊查询,精准查询)
*
* 需求:根据name(指定为"张三")查询简历
*/
Specification<Resume> specification = new Specification<Resume>() {
@Override
public Predicate toPredicate(Root<Resume> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
// 获取到name属性
Path<Object> name = root.get("name");
// 使用CriteriaBuilder针对name属性构建条件(精准查询)
Predicate predicate = criteriaBuilder.equal(name, "张三");
return predicate;
}
};
Optional<Resume> optional = resumeDao.findOne(specification);
Resume resume = optional.get();
System.out.println(resume);
}
多个条件组合查询
@Test
public void testSpecficationMultiCon(){
/**
* 需求:根据name(指定为"张三")并且,address 以"北"开头(模糊匹配),查询简历
*/
Specification<Resume> specification = new Specification<Resume>() {
@Override
public Predicate toPredicate(Root<Resume> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
// 获取到name属性
Path<Object> name = root.get("name");
Path<Object> address = root.get("address");
// 条件1:使用CriteriaBuilder针对name属性构建条件(精准查询)
Predicate predicate1 = criteriaBuilder.equal(name, "张三");
// 条件2:address 以"北"开头(模糊匹配)
Predicate predicate2 = criteriaBuilder.like(address.as(String.class), "北%");
// 组合两个条件
Predicate and = criteriaBuilder.and(predicate1, predicate2);
return and;
}
};
Optional<Resume> optional = resumeDao.findOne(specification);
Resume resume = optional.get();
System.out.println(resume);
}
Spring Data JPA 旨在通过将工作量减少到实际需要的数量来显著改进数据访问层的实现。