一 概述
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范
的基础上封装
的一套JPA应用
框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
推荐组合
Spring Data JPA 让我们解脱了DAO层
的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)
完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦
特性
SpringData Jpa 极大简化
了数据库访问层代码。 如何简化的呢? 使用了SpringDataJpa,我们的dao层中只需要写接口
,就自动具有了增删改查、分页查询等方法。
和其他框架的关系
Spring Data JPA 与 JPA和hibernate之间的关系
JPA是一套规范,内部是有接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA的API编程,意味着站在更高的角度上看待问题(面向接口编程)
Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装
,是在JPA规范下的专门用来进行数据持久化的解决方案。
注意
真正干活的还是hibernate
二 搭建环境
依赖
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zyc</groupId>
<artifactId>springDateJpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springDateJpa</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
dao
使用Spring Data JPA,只需要按照框架的规范提供dao接口,不需要实现类
就可以完成数据库的增删改查、分页查询等方法的定义,极大的简化了我们的开发过程。
规范
- 创建一个Dao层接口,并实现
JpaRepository
和JpaSpecificationExecutor
- 提供相应的泛型
package com.zyc.springDateJpa.dao;
import com.zyc.springDateJpa.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
* 符合SpringDataJpa的dao层接口规范
* JpaRepository<操作的实体类类型,实体类中主键属性的类型>
* * 封装了基本CRUD操作
* JpaSpecificationExecutor<操作的实体类类型>
* * 封装了复杂查询(分页)
*/
public interface CustomerDao extends JpaRepository<Customer,Long> ,JpaSpecificationExecutor<Customer> {
public Customer findByCustName(String custName);
}
测试
package com.zyc.springDateJpa;
import com.zyc.springDateJpa.dao.CustomerDao;
import com.zyc.springDateJpa.entity.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDateJpaApplicationTests {
@Autowired
private CustomerDao customerDao;
@Test
public void contextLoads() {
Customer c = new Customer();
c.setCustName("zhuyc");
customerDao.save(c);
}
}
三 CRUD
查询
Optional<Customer> c = customerDao.findById(1l);
System.out.println(c.get());
保存/更新
svae:保存或者更新
- 根据传递的对象
是否存在
主键id,如果没有id主键属性:保存 - 存在id主键属性,根据id
查询
数据,更新
数据.如果没查到,还是更新
Customer c = new Customer();
c.setCustName("zhuyc");
customerDao.save(c);
删除
根据id删除
springDataJpa会先查询,再删除;没查到,会报错
customerDao.deleteById(2l);
查询所有
List<Customer> list = customerDao.findAll();
for(Customer customer : list) {
System.out.println(customer);
}
四 执行过程
在SpringDataJpa中我们只需要声明接口,框架会自动帮我们通过jdk动态代理
生成接口实现类,然后注入导spring中。
动态代理对象中一般都是默认调用SimpleJpaRepository
中的方法来工作的
五 复杂查询
查询总数
long count = customerDao.count();
查询id是否存在
boolean exists = customerDao.existsById(4l);
getOne
- findById:
em.find() :立即加载 - getOne:
em.getReference :延迟加载;返回的是一个客户的动态代理对象. 什么时候用,什么时候查询
Customer customer = customerDao.getOne(4l);
JPQL
@Query
注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可
@Query(value="from Customer where custName = ?1")
public Customer findJpql(String custName);
占位符
通过索引
/**
* 案例:根据客户名称和客户id查询客户
* jpql: from Customer where custName = ? and custId = ?
*
* 对于多个占位符参数
* 赋值的时候,默认的情况下,占位符的位置需要和方法参数中的位置保持一致
*
* 可以指定占位符参数的位置
* ? 索引的方式,指定此占位的取值来源
*/
@Query(value = "from Customer where custName = ?2 and custId = ?1")
public Customer findCustNameAndId(Long id, String name);
实现更新
@Query
: 代表的是进行查询
@Modifying
:当前执行的是一个更新操作.有点组合的感觉,补充@Query
/**
*
* sql :update cst_customer set cust_name = ? where cust_id = ?
* jpql : update Customer set custName = ? where custId = ?
*/
@Query(value = " update Customer set custName = ?2 where custId = ?1 ")
@Modifying
public void updateCustomer(long custId, String custName);
@Rollback
设置是否自动回滚,本地测试:默认不回滚。
@Test
@Transactional //添加事务的支持
@Rollback(value = true)
public void testUpdateCustomer() {
customerDao.updateCustomer(4l,"黑马程序员");
}
sql查询
@Query
有一个nativeQuery属.
/**
* sql : select * from cst_customer;
* Query : 配置sql查询
* value : sql语句
* nativeQuery : 查询方式
* true : sql查询
* false:jpql查询(默认)
*
*/
//@Query(value = " select * from cst_customer" ,nativeQuery = true)
@Query(value="select * from cst_customer where cust_name like ?1",nativeQuery = true)
public List<Object [] > findSql(String name);
查询Map
有时会用到
@Test
public void testNativeQuery(){
Query query = em.createNativeQuery("select * from cst_customer ");
List<Map<String,Object>> list = query.unwrap(NativeQuery.class).addScalar("cust_id", StandardBasicTypes.LONG).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
for(Map<String,Object> map:list){
System.out.println(map.values());
}
}
方法命名规则查询
按照Spring Data JPA 定义的规则,查询方法以 findBy
开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。
/**
* 方法名的约定:
* findBy : 查询
* 对象中的属性名(首字母大写) : 查询的条件
* CustName
* * 默认情况 : 使用 等于的方式查询
* 特殊的查询方式
*
* findByCustName -- 根据客户名称查询
*
* 再springdataJpa的运行阶段
* 会根据方法名称进行解析 findBy from xxx(实体类)
* 属性名称 where custName =
*
* 1.findBy + 属性名称 (根据属性名称进行完成匹配的查询=)
* 2.findBy + 属性名称 + “查询方式(Like | isnull)”
* findByCustNameLike
* 3.多条件查询
* findBy + 属性名 + “查询方式” + “多条件的连接符(and|or)” + 属性名 + “查询方式”
*/
public Customer findByCustName(String custName);
public Customer findByCustNameLikeAndCustIndustry(String custName, String custIndustry);
具体的关键字,使用方法和生产成SQL如下表所示
参考
- 黑马Spring Data Jpa教学资料