Spring Data JPA的简介
Spring Data JPA 是 Spring 基于JPA 规范的基础上封装的一套 JPA 应用框架,可使开发者用极简的 代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能!学习并使用 Spring Data JPA 可以极大提高开发效率。
说明:Spring Data JPA 极大简化了数据访问层代码。
如何简化呢?使用了Spring Data JPA,我们Dao层中只需要写接口,不需要写实现类,就自动具有 了增删改查、分页查询等方法。 使用Spring Data JPA 很多场景下不需要我们自己写sql语句
Spring Data JPA,JPA规范和Hibernate之间的
JPA 是一套规范,内部是由接口和抽象类组成的,Hiberanate 是一套成熟的 ORM 框架,而且 Hiberanate 实现了 JPA 规范,所以可以称 Hiberanate 为 JPA 的一种实现方式,我们使用JPA 的 API 编 程,意味着站在更高的角度去看待问题(面向接口编程)。 Spring Data JPA 是 Spring 提供的一套对 JPA 操作更加高级的封装,是在 JPA 规范下的专门用来进行数 据持久化的解决方案。
Spring Data JPA 应用
需求:使用 Spring Data JPA 完成对 tb_resume 表(简历表)的Dao 层操作(增删改查,排序, 分页等)
数据表设计:
初始化Sql语句 :
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_resume
-- ----------------------------
DROP TABLE IF EXISTS `tb_resume`;
CREATE TABLE `tb_resume` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`address` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) 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;
Spring Data JPA 开发步骤如下
1、构建工程 创建工程导入坐标(Java框架于我们而言就是一堆jar)
2、配置 Spring 的配置文件(配置指定框架执行的细节)
3、编写实体类 Resume,使用 JPA 注解配置映射关系
4、编写一个符合 Spring Data JPA 的 Dao 层接口(ResumeDao接⼝)
5、操作 ResumeDao 接口对象完成 Dao 层开发
Spring Data JPA 开发实现 如下
1.1、创建工程
1.2、导入坐标
Spring相关的包 ,spring-data-jpa相关的包、hibernate相关jar包、hibernate对jpa的实现jar、mysql驱动、druid连接池等。
<dependencies>
<!--单元测试jar-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--spring-data-jpa 需要引入的jar,start-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.1-b04</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
<!--spring-data-jpa 需要引入的jar,end-->
<!--spring 相关jar,start-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<!--spring对orm框架的支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<!--spring 相关jar,end-->
<!--hibernate相关jar包,start-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.0.Final</version>
</dependency>
<!--hibernate对jpa的实现jar-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.0.Final</version>
</dependency>
<!--hibernate相关jar包,end-->
<!--mysql 数据库驱动jar-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!--spring-test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
</dependencies>
<!--指定编译级别-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
2.1、配置 Spring 的配置文件applicationContext.xml
1、创建数据库连接池Druid
2、配置一个JPA中非常重要的对象,entityManagerFactory
3、通过上面的配置去扫描响应的包
4、事物管理器的配置
5、声明式事物的配置
6、扫描spring包
<?xml version="1.0" encoding="UTF-8"?>
<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
">
<!--对Spring和SpringDataJpa的配置-->
<!-- 1、创建数据库连接池Druid-->
<!--1.1引入外部配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--1.2将第三方jar中的bean也配置在xml中-->
<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>
<!--2、配置一个JPA中非常重要的对象,entityManagerFactory
entityManager类似于mybatis中的SqlSession
entityManagerFactory类似于Mybatis中的SqlSessionFactory
-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--需要配置一些细节-->
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--配置扫描包(扫描实体类pojo的包)-->
<property name="packagesToScan" value="com.slin.edu.pojo"/>
<!--指定jpa(它是一个规则,它不是具体执行的)的具体实现,也就是hibernate-->
<property name="persistenceProvider" >
<!--是由hibernate去执行-->
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!--配置具体的provider,这里指定是hibernate,那就需要配置hibernate的执行细节-->
<property name="jpaVendorAdapter" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--定义hibernate框架的一些细节-->
<!--
配置数据表是否自动创建
因为我们会建立pojo和数据表之间的映射关系
程序启动时,如果数据表还没有创建,是否要程序给创建一下
-->
<property name="generateDdl" value="false"/>
<!--指定数据库的类型, hibernate本身是个dao层框架,
可以支持多种数据库类型的,这里就指定本次使用的什么数据库-->
<property name="database" value="MYSQL"/>
<!--
配置数据库的方言
hiberante可以帮助我们拼装sql语句,
但是不同的数据库sql语法是不同的,所以需要我们注入具体的数据库方言 -->
<!-- <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
<!--是否显示sql.操作数据库时,是否打印sql-->
<property name="showSql" value="true"/>
</bean>
</property>
</bean>
<!--3、通过上面的配置去扫描响应的包-->
<!-- <jpa:repositories> 配置jpa的dao层细节
base-package:指定dao层接口所在包 -->
<jpa:repositories base-package="com.slin.edu.dao" entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
<!-- 4、事物管理器的配置-->
<!--jdbcTemplate/mybatis 使用的是DataSourceTransactionManager
jpa规范:JpaTransactionManager-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--5、声明式事物的配置 -->
<!--进行update、delete的时候,就的在service层用到事物,service事物生效的前提的配这个 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--6、扫描spring包-->
<context:component-scan base-package="com.slin.edu"/>
</beans>
2.2、配置 Spring 的配置文件jdbc.properties
jdbc.username=root
jdbc.password=
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/jpa
2.3、编写测试数据源是否获取成功
//测试数据源是否获取成功
@Test
public void testDataSource() throws SQLException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
3.1、编写实体类 Resume,使用JPA 注解配置映射关系
/**
* @Description:*
* * 简历实体类(在类中要使⽤注解建⽴实体类和数据表之间的映射关系以及属性和字段的映射关系)
* * * 1、实体类和数据表映射关系
* * * @Entity
* * * @Table
* * * 2、实体类属性和表字段的映射关系
* * * @Id 标识主键
* * * @GeneratedValue 标识主键的⽣成策略
* * * @Column 建⽴属性和字段映射
* @Author: shanglin
* @Date: 2020-11-05 23:04
*简历实体类(在类中要使用注解建立实体类和数据表之间的映射关系以及属性和字段的映射关系)
*/
@Entity
@Table(name = "tb_resume")
public class Resume {
/**成策略经常使⽤的两种:
* GenerationType.IDENTITY:依赖数据库中主键⾃增功能 Mysql
* GenerationType.SEQUENCE:依靠序列来产⽣主键 Oracle
*/
@Id //标识主键
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id") //@Column 属性和字段映射
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 + '\'' +
'}';
}
}
4.1、编写 ResumeDao 接口实现查询数据
创建一个ResumeDao借口继承JpaRepository接口,在 @Query 注解中添加参数 nativeQuery=true,即可执行本地查询
/**
*一个符合SpringDataJpa要求的Dao层接⼝是需要继承JpaRepository和JpaSpecificationExecutor
*
* JpaRepository<操作的实体类类型,主键类型>
* 封装了基本的CRUD操作
*
* JpaSpecificationExecutor<操作的实体类类型>
* 封装了复杂的查询(分页、排序等)
*/
public interface ResumeDao extends JpaRepository<Resume,Long>, JpaSpecificationExecutor<Resume> {
/** 参数后面要跟参数的位置是第一个还是第二个
传递参数的方式1:使用占位符, 此方式要求形参与定义的 JPQL 的参数位置一致*/
@Query("from Resume where id=?1 and name=?2")
public List<Resume> findByJpql(Long id, String name);
/**传递参数的方式2:使用命名参数, 此方式形参与定义的 JPQL 的参数位置不必一致*/
@Query("from Resume where id=:id and name=:name")
public List<Resume> findByJpql2(Long id, String name);
/**
* 使用原生的sql语句查询,需要将nativeQuery属性设置为true,默认为false(jpql)
*/
@Query(value = "select * from tb_resume where name like ?1 and address like ?2",nativeQuery = true)
public List<Resume> findBySql(String name,String address);
/**
* 方法命名规则查询
* 按照name模糊查询(like)
* 方法名以findBy开头
* -属性名(首字母大写如Name)
* -查询方式(模糊查询、等价查询),如果不写查询方式,默认等价查询
*/
public List<Resume> findByNameLikeAndAddress(String name,String address);
}
4.2、客户端测试类
对4.1的操作 ResumeDao 接口的测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class ResumeDaoTest {
@Autowired
private ResumeDao resumeDao;
/**
* dao层接口调用,分成两块:
* 1、基础的增删改查
* 2、专⻔针对查询的详细分析使⽤
*/
@Test
public void testFindById(){
// 早期的版本 dao.findOne(id);
/*
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);
}
}
5.1、实现UPDATE 和 DELETE 操作
1、使用 @Modifying 配合 @Query 可以完成 UPDATE 和 DELETE 操作
2、可以通过自定义 JPQL 完成 UPDATE 和 DELETE 操作,注:JPQL 不支持使用 INSERT
3、在 @Query 中编写 JPQL 语句,但必须使用 @Modifying 注解修饰,以通知 SpringData 此操作是一个
UPDATE 或 DELETE 操作
4、UPDATE 或 DELETE 操作需要使用事务,所以需要定义 service 层,在 service 层方法上添加事务操作
要求 : 必须有配置事物注解
<tx:annotation-driven transaction-manager=”transactionManager”/>
在service层
@Service
public class ResumeService {
@Autowired
private ResumeDao resumeDao;
@Transactional
public void updateResume(Long id,String name,String address){
resumeDao.updateResume(id,name,address);
}
}
在dao层
/*可以通过自定义 JPQL 完成 UPDATE 和 DELETE 操作,注:JPQL 不支持使用 INSERT
在 @Query 中编写 JPQL 语句,但必须使用 @Modifying 注解修饰,
以通知 SpringData 此操作是一个 UPDATE 或 DELETE 操作
UPDATE 或 DELETE 操作需要使用事务,所以需要定义 service 层,在 service 层方法上添加事务操作
默认情况下,SpringData 的每个方法上都有事务,但都是只读事务,他们不能完成修改操作 */
@Modifying
@Query("UPDATE Resume p SET p.name = :name,p.address = :address WHERE p.id = :id")
void updateResume(@Param("id") Long id, @Param("name") String name, @Param("address") String address);
5.2、编写update的测试类
@Test
public void testUpdate(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ResumeService bean = ctx.getBean(ResumeService.class);
bean.updateResume(1L,"哈哈","南宁");
}
6.1、实现保存对象
通过反射,只需要传入class对象和字段信息有时候还可以传入对应的值
@Test
public void testSave1() throws Exception{
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ResumeDao bean = ctx.getBean(ResumeDao.class);
String[] str={"name","address","phone"};/*传入属性或者在数据库获取属性*/
/*传入全限定类名可能不太存在,传入class对象倒是合理,因为service层就是自己编写的啊*/
String className ="com.slin.edu.pojo.Resume";
Class<?> aClass = Class.forName(className);
Class<?> resumeClass = Resume.class;
Object o = testSave2(str, resumeClass);
Resume resume =(Resume) o;
bean.save(resume);
}
public Object testSave2(String[] str,Class<?> aClass) throws Exception {
Object o = aClass.newInstance();
// 目前这个值是随机的,也可以通过传入参数进行赋值的,这样就不需要手写new了
for (String s : str) {
Field nameField = aClass.getDeclaredField(s);
nameField.setAccessible(true);
int intrandom = new Random().nextInt(100);
nameField.set(o,intrandom+"");
}
return o;
}
7.1、编写并实现分页
dao层要继承分页接口
/*实现分页操作(带排序):(只读事务,不需要再 service 层中编写)*/
public interface ResumePaging extends PagingAndSortingRepository<Resume,Long> {
}
不用经过service层,可以直接在测试模块调用
mport org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
更详细的分页可进入PageRequest类查看
//测试 PagingAndSortRepository 的 findAll(Pageable pageable) 方法,进行分页
@Test
public void testPagingAndSortingRepository() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ResumePaging bean = ctx.getBean(ResumePaging.class);
//pageNo 从 0 开始
int pageNo = 1 - 0;
int pageSize = 1;
//Sort 封装了排序信息,Order 指明具体是根据哪一个属性进行升序或者降序
Sort.Order order1 = new Sort.Order(Sort.Direction.DESC, "id");
Sort.Order order2 = new Sort.Order(Sort.Direction.ASC, "name");
Sort sort = new Sort(order1, order2);
//Pageable 接口通常使用其 PageRequest 实现类,其中封装了需要分页的信息
PageRequest pageable = new PageRequest(pageNo, pageSize, sort);
Page<Resume> page = bean.findAll(pageable);
System.out.println("总共有 " + page.getTotalElements() + " 条记录");
System.out.println("总共有 " + page.getTotalPages() + " 页");
System.out.println("当前页为:" + (page.getNumber() + 1));
System.out.println("当前的 List: " + page.getContent());
System.out.println("当前页的总记录数为:" + page.getNumberOfElements());
}
注意细节1:
首先Repository 是一个空接口,如果接口实现了 Repository,则该接口会被 IOC 容器识别为一个 Repository Bean,纳入到 IOC 容器中
Repository 接口的实现类如下:
1)CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
2)PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
3)JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
4)自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
注:JpaSpecificationExecutor: 不属于Repository体系,实现一组 JPA Criteria 查询相关的方法
注意细节2:
SpringData 的方法定义规范SpringData 的方法定义规范
1.不能随便声明,需要符合一定的规范
2.查询的方法以 find、read、get 开头
3.涉及条件查询时条件的属性要以关键字连接,条件属性首字母大写
1)SpringData 支持的关键字
整体模块如下
1、引入依赖
2、添加配置文件(重点)
3、创建pojo
4、创建接口(重点)
5、测试