SpringBoot项目中使用Jpa(SpringDataJpa)
前言
-
请查看博主的SpringDataJpa系列文章。欢迎关注!
-
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关 系,并将运行期的实体对象持久化到数据库中。
-
在ORM框架中,Hibernate是一支很大的部队,使用很广泛,也很方便,能力也很强,同时Hibernate也是和JPA整 合的比较良好,我们可以认为JPA是标准,事实上也是,JPA几乎都是接口,实现都是Hibernate在做,宏观上面看,在 JPA的统一之下Hibernate很良好的运行。
-
我们都知道Spring的强大,到目前为止,企业级应用Spring几乎是无所不能,无所不在,已经是事实上的标准了, 企业级应用不使用Spring的几乎没有。而Spring整合第三方框架的能力又很强,他要做的不仅仅是个最早的IOC容器这 么简单一回事,现在Spring涉及的方面太广,主要是体现在和第三方工具的整合上。而在与第三方整合这方面,Spring 希望把持久化这块内容也拿下。于是就有了Spring-data-**这一系列包。包括,Spring-data-jpa,Spring-data- template,Spring-data-mongodb,Spring-data-redis,还有个民间产品,mybatis-spring,和前面类似,这是和 mybatis整合的第三方包,这些都是干的持久化工具干的事儿。
-
这里介绍Spring-data-jpa,表示与Jpa的整合
-
SpringData:其实SpringData就是Spring提供了一个操作数据的框架。而SpringData JPA只是SpringData框架下的一个基于JPA标准操作数据的模块,总得包括以下:
- ORM映射:支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系
- API:操作实体对象来执行CRUD操作
- 查询语言:通过面向对象而非面向数据库的查询语言(JPQL)查询数据,避免程序的SQL语句紧密耦合
-
SpringDataJPA:基于JPA的标准数据进行操作。简化操作持久层的代码。只需要编写接口就可以。
-
后面博主会陆续写出Jpa的关联关系和增删改查以及连表查询操作,欢迎关注
什么是Spring Data Jpa
Spring Data Jpa是Spring Data家族的一部分,Spring Data JPA相对于Java EE中的JPA,配置更简单,以轻量级的方式实现了部分在 EJB 容器环境下才具有的功能,将 EntityManager 的创建与销毁、事务管理等代码抽取出来,并由其统一管理,并且极大的简化了数据库访问层的代码。
Spring Data包含众多子项目除了JPA还有Spring Data MongoDB等等
SpringJpa的运行原理
- SpringJPA的全称是Spring Data JPA 。其中JPA是Java Persistence API的缩写(Java持久化API),是
SUN公司推出的一套接口,一套标准,Hibernate是一个具体的ORM的持久层框架(类似于Mybatis框
架)实现了JPA接口 。 - Spring Data是Spring开发团队提供的一套标准API和不同持久层整合技术实现。Spring Data的出现就
是为了简化、统一持久层的各种实现技术API。 (注:Spring Data在项目里以spring-data-commons这个jar存在 ) - Spring Data JPA既实现了Spring Data接口又实现了JPA接口,也是为了简化持久层的开发。
(注:Spring Data JPA在项目里以spring-data-jpa这个jar存在)
SpringJpa优点
- 提供统一的接口,可避免我们再次重复的编写基础的DAO类;
- 遵循JPA规范,同时也提供了灵活的数据访问方式;
- 通过方法名即可自动生成HQL语句;
- 通过接口自动注入实现类,实现非常简单。
Spring Data JPA与MyBatis对比
Spring Data JPA与MyBatis对比,实际上就是Hibernate与MyBatis的对比,具体如下:
从基本概念和框架目标上看,两个框架差别还是很大的。 hibernate是一个自动化更强、更高级的框架,毕竟在java代码层面上,省去了绝大部分sql编写,取而代之的是 用面向对象的方式操作关系型数据库的数据。 MyBatis则是一个能够灵活编写sql语句,并将sql的入参和查询结果映射成POJOs的一个持久层框架。所以,从表 面上看,hibernate能方便、自动化更强,而MyBatis 在Sql语句编写方面则更灵活自由。
注:至于这两个框架用哪一个,可以根据自己的掌握情况或者使用场景进行选择。我个人推荐使用Spring
Data JPA,因为的确很简单,符合敏捷开发要求。
SpringBoot引入SpringDataJpa依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
在yml文件配置如下,按需索取
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
username: root
password: root
jpa:
database: mysql
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
hibernate:
ddl-auto: update # 一般使用update
# create: 每次运行程序时,都会重新创建表,故而数据会丢失
# create-drop: 每次运行程序时会先创建表结构,然后待程序结束时清空表
# upadte: 每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新(推荐使用)
# validate: 运行程序会校验数据与数据库的字段类型是否相同,字段不同会报错
# none: 禁用DDL处理
这里贴出来博主的一些配置:
<?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>
<groupId>com.jmccms</groupId>
<artifactId>Jmccms</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Jmccms</name>
<url>https://repo.spring.io/milestone</url>
<description>CYJ:ChenYongJia 服务提供者Jmccms</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- 项目属性配置信息 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!--引入spring-boot 2.1.1.RELEASE ,SpringCloud的版本为 Greenwich.M3-->
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<mysql-connector>5.1.39</mysql-connector>
<junit-version>4.12</junit-version>
<druid>1.0.18</druid>
<!--<activiti.version>6.0.0</activiti.version>-->
</properties>
<dependencies>
<!-- 引入web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 引入AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.3</version>
</dependency>-->
<!-- SpringCloud Eureka 注册中心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- SpringCloud Hystrix 微服务容错监控组件:断路器,依赖隔离,服务降级,服务监控 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 权限控制Spring-Oauth2.0的认证模式依赖暂时不用,由于一些注解和API从spring security5.0中移除,所以需要导入下面的依赖包 -->
<!-- spring-cloud-starter-oauth2 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- SpringCloud Feign 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Spring Boot JPA 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 引入test测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Junit 单元测试 依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- 引入redis数据库依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 引入jdbc链接依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 引入security权限依赖模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 引入Thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 引入mysql链接依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector}</version>
</dependency>
<!-- 引入 Lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Boot devtools 热部署 依赖 -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>-->
<!-- alibaba的fastjson工具 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<!-- 引入Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!-- 阿里开源EXCEL -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.2-beta5</version>
</dependency>
<!--<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>-->
<!-- 用于字符串工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<!--<version>3.8.1</version>-->
<version>3.6</version>
<scope>provided</scope>
</dependency>
<!-- 使用httpclient获取天气信息 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
<!-- 引入工作流 -->
<!--<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
</dependency>-->
<!--<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-actuator</artifactId>
<version>${activiti.version}</version>
</dependency>-->
<!--<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-rest</artifactId>
<version>${activiti.version}</version>
</dependency>-->
<!-- 整合swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- 整合swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- eu帮助类 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
</dependencies>
<!-- SpringCloud 所有子项目 版本集中管理.MS:统一所有SpringCloud依赖项目的版本 依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<!-- SpringBoot 2.1.X 以上版本这样配置 -->
<version>${spring-cloud.version}</version>
<!-- <version>Dalston.RC1</version> -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- SpringBoot 项目打jar包的Maven插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- SpringBoot项目打包jar名称 -->
<finalName>jmccms</finalName>
</build>
<!-- SpringCloud 官方远程仓库.MS:部分本地仓库和镜像仓库没有SpringCloud子项目依赖。 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
使用Spring Data Jpa增删改查(当然一般都和lombok结合使用)
第一步先创建你的数据库和配置文件保持一致,Jpa将自动帮你建表
package com.jmccms.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
/**
* @Description: 用户类
* @BelongsProject: Jmccms
* @BelongsPackage: com.jmccms.entity
* @Author: ChenYongJia
* @CreateTime: 2019-05-02 15:32
* @Email chen87647213@163.com
*/
@Getter
@Setter
@AllArgsConstructor // 自动所有参数的构造方法方法
@NoArgsConstructor // 自动无参的构造方法方法
@Builder
@Entity
@Table(name = "jmccms_user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@OrderBy
@Column(columnDefinition = "bigint(19) unsigned COMMENT '用户id'")
private Long userId;
@Column(columnDefinition = "varchar(64) NOT NULL COMMENT '用户名称' ")
private String userName;
@Column(columnDefinition = "varchar(100) NOT NULL COMMENT '用户密码' ")
private String userPassWord;
@Column(columnDefinition = "datetime COMMENT '用户创建时间' ")
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss") //日期格式化为中国的时区 东8区
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") //接受::字符串日期需要格式化为日期类型
private Date userCreateTime;
@Column(columnDefinition = "datetime COMMENT '用户最后一次登录时间' ")
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss") //日期格式化为中国的时区 东8区
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") //接受::字符串日期需要格式化为日期类型
private Date userLastLoginTime;
@Column(columnDefinition = "timestamp COMMENT '最后一次修改时间'", nullable = false, updatable = false, insertable = false)
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss") //日期格式化为中国的时区 东8区
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") //接受::字符串日期需要格式化为日期类型
private Timestamp userLastUpdateTime;
@Column(columnDefinition = "varchar(64) NOT NULL COMMENT '创建人' ")
private String userFounder;
@Column(columnDefinition = "varchar(64) COMMENT '修改人' ")
private String userUpdateMan;
@JsonIgnore
@ManyToMany(fetch = FetchType.EAGER) // 指定多对多关系
@Cascade(value = { CascadeType.ALL }) // 设置级联关系
@JoinTable(name = "jmccms_user_role", // 指定第三张中间表名称
joinColumns = { @JoinColumn(name = "user_id") }, // 本表主键userId与第三张中间表user_role_tb的外键user_role_tb_user_id对应
inverseJoinColumns = { @JoinColumn(name = "role_id") }) // 多对多关系另一张表与第三张中间表表的外键的对应关系
@NotFound(action = NotFoundAction.IGNORE) // NotFound : 意思是找不到引用的外键数据时忽略,NotFound默认是exception
private Set<Role> rolesSet = new HashSet<>();// 用户所拥有的角色集合
}
- 建立数据库访问层
- 使用Spring Data JPA建立数据库十分简单,只需要定义一个继承了JpaRepository的接口,下面是博主项目的一个类,可以参照
package com.jmccms.dao;
import com.jmccms.entity.User;
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 org.springframework.data.repository.query.Param;
import javax.transaction.Transactional;
import java.util.List;
/**
* @Description: 用户接口
* @BelongsProject: Jmccms
* @BelongsPackage: com.jmccms.dao
* @Author: ChenYongJia
* @CreateTime: 2019-05-02 18:05
* @Email chen87647213@163.com
*/
public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
/**
* 根据用户名和密码查询用户
* @return
*/
User findByUserNameAndUserPassWord(String userName, String userPassWord);
/**
* 根据用户名查询用户
* @return
*/
User findByUserName(String userName);
/**
* 根据用户id查询用户信息
* @param userId
* @return
*/
User findByUserId(Long userId);
/**
* 批量删除用户信息
*
* @param userList
* @return
*/
@Query(value = "DELETE FROM jmccms_user WHERE user_id IN (:userList)", nativeQuery = true)
@Modifying
@Transactional
Integer deleteUser(@Param(value = "userList") List<String> userList);
/**
* 根据ID查询用户角色
*
* @param userId
* @return
*/
@Query(value = "SELECT role_id FROM jmccms_user_role WHERE 1=1 AND user_id=:usersId ", nativeQuery = true)
List<Long> getUserRole(@Param(value = "usersId") Long userId);
}
- 继承了JpaRepository就相当于有了下面的数据访问操作方法,这些都是Spring Data Jpa封装好的。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.data.jpa.repository;
import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAllById(Iterable<ID> var1);
<S extends T> List<S> saveAll(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
支持的关键字、示例及JPQL片段如下表所示:
Keyword | Sample | JPQL Snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | indByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … findByFirstnameNotLike |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
… | … | … |
更多详细内容请参照:官方文档4.4.3. Property Expressions
好了到这里也该结束了,不能全部给你们不是,各位要自己多动手才能学到真正的东西。加油各位
最后
-
更多参考精彩博文请看这里:《陈永佳的博客》
-
喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!