【Spring Boot】Java 持久层 API:JPA

Java 持久层 API:JPA

  • [1.Spring Data](#1.Spring Data)
    • [1.1 主要模块](#1.1 主要模块)
    • [1.2 社区模块](#1.2 社区模块)
  • 2.JPA
  • [3.使用 JPA](#3.使用 JPA)
    • [3.1 添加 JPA 和 MySQL 数据库的依赖](#3.1 添加 JPA 和 MySQL 数据库的依赖)
    • [3.2 配置数据库连接信息](#3.2 配置数据库连接信息)
  • [4.了解 JPA 注解和属性](#4.了解 JPA 注解和属性)
    • [4.1 常用注解](#4.1 常用注解)
    • [4.2 映射关系的注解](#4.2 映射关系的注解)
    • [4.3 映射关系的属性](#4.3 映射关系的属性)
  • [5.用 JPA 构建实体数据表](#5.用 JPA 构建实体数据表)

1.Spring Data

Spring Data 是 Spring 的一个子项目,旨在统一和简化各类型数据的持久化存储方式,而不拘泥于是关系型数据库还是 NoSQL 数据库。

无论是哪种持久化存储方式,数据访问对象Data Access ObjectsDAO)都会提供对对象的增加、删除、修改和查询的方法,以及排序和分页方法等。

Spring Data 提供了基于这些层面的统一接口(如:CrudRepository、PagingAndSortingRepository),以实现持久化的存储。

Spring Data 包含多个子模块,主要分为主模块和社区模块。

1.1 主要模块

模块名称 作用
Spring Data Commons 提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化。
Spring Data JDBC 提供了对 JDBC 的支持,其中封装了 JDBCTemplate。
Spring Data JDBC Ext 提供了对 JDBC 的支持,并扩展了标准的 JDBC,支持 Oracle RAD、高级队列和高级数据类型。
Spring Data JPA 简化创建 JPA 数据访问层和跨存储的持久层功能。
Spring Data KeyValue 集成了 Redis 和 Riak,提供多个常用场景下的简单封装,便于构建 key-value 模块。
Spring Data LDAP 集成了 Spring Data repository 对 Spring LDAP 的支持。
Spring Data MongoDB 集成了对数据库 MongoDB 支持。
Spring Data Redis 集成了对 Redis 的支持。
Spring Data REST 集成了对 RESTful 资源的支持。
Spring Data for Apache Cassandra 集成了对大规模、高可用数据源 Apache Cassandra 的支持。
Spring Data for Apace Geode 集成了对 Apache Geode 的支持。
Spring Data for Apache Solr 集成了对 Apache Solr 的支持。
Spring Data for Pivotal GemFire 集成了对 Pivotal GemFire 的支持。

1.2 社区模块

模块名称 作用
Spring Data Aerospike 集成了对 Aerospike 的支持
Spring Data ArangoDB 集成了对 ArangoDB 的支持。
Spring Data Couchbase 集成了对 Couchbase 的支持。
Spring Data Azure Cosmos DB 集成了对 Azure Cosmos 的支持。
Spring Data Cloud Datastore 集成了对 Google Datastore 的支持
Spring Data Cloud Spanner 集成了对 Google Spanner 的支持。
Spring Data DynamoDB 集成了对 DynamoDB 的支持。
Spring Data Elasticsearch 集成了对搜索引擎框架 Elasticsearch 的支持。
Spring Data Hazelcast 集成了对 Hazelcast 的支持。
Spring Data Jest 集成了对基于 Jest REST client 的 Elasticsearch 的支持。
Spring Data Neo4j 集成了对 Neo4j 数据库的支持。
Spring Data Vault 集成了对 Vault 的支持。

2.JPA

JPA(Java Persistence APl)是 Java 的持久化 API,用于对象的持久化。它是一个非常强大的 ORM 持久化的解决方案,免去了使用 JDBCTemplate 开发的编写脚本工作。JPA 通过简单约定好接口方法的规则自动生成相应的 JPQL 语句,然后映射成 POJO 对象。

JPA 是一个规范化接口,封装了 Hibernate 的操作作为默认实现,让用户不通过任何配置即可完成数据库的操作。JPA、Spring Data 和 Hibernate 的关系如下图所示。

Hibernate 主要通过 hibernate-annotationhibernate-entitymanagerhibernate-core 三个组件来操作数据。

  • hibernate-annotation:是 Hibernate 支持 annotation 方式配置的基础,它包括标准的 JPAannotation、Hibernate 自身特殊功能的 annotation。
  • hibernate-core:是 Hibernate 的核心实现,提供了 Hibernate 所有的核心功能。
  • hibernate-entitymanager:实现了标准的 JPA,它是 hibernate-core 和 JPA 之间的适配器,它不直接提供 ORM 的功能,而是对 hibernate-core 进行封装,使得 Hibernate 符合 JPA 的规范。

如果要 JPA 创建《Java 的数据库连接模板:JDBCTemplate》中 "2.2 新建实体类" 里的实体,可使用以下代码来实现。

java 复制代码
@Data
@Entity
public class User {
	private int id;
	@Id
	// id 的自增由数据库自动管理
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private String username;
	private String password;
}

对比 JPA 与 JDBCTemplate 创建实体的方式可以看出:JPA 的实现方式简单明了,不需要写映射(支持自定义映射),只需要设置好属性即可。id 的自增由数据库自动管理,也可以由程序管理,其他的工作 JPA 自动处理好了。

3.使用 JPA

要使用 JPA,只要加入它的 Starter 依赖,然后配置数据库连接信息。

3.1 添加 JPA 和 MySQL 数据库的依赖

下面以配置 JPA 和 MySQL 数据库的依赖为例,具体配置见以下代码:

xml 复制代码
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

3.2 配置数据库连接信息

Spring Boot 项目使用 MySQL 等关系型数据库,需要配置连接信息,可以在 application.properties 文件中进行配置。以下代码配置了与 MySQL 数据库的连接信息:

xml 复制代码
spring.datasource.url=jdbc:mysql://127.0.0.1/book?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql= true
  • spring.datasource.username:要填写的数据库用户名。
  • spring.datasource.password:要填写的数据库密码。
  • spring.jpa.show-sql:开发工具的控制台是否显示 SQL语句,建议打开。
  • spring.jpa.properties.hibernate.hbm2ddl.auto:hibernate 的配置属性,其主要作用是自动创建、更新、验证数据库表结构。该参数的几种配置如下表所示。
属性 说明
create 每次加载 Hibernate 时都会删除上一次生成的表,然后根据 Model 类再重新生成新表,哪怕没有任何改变也会这样执行,这会导致数据库数据的丢失。
create-drop 每次加载 Hibernate 时会根据 Model 类生成表,但是 sessionFactory 一旦关闭,表就会自动被删除。
update 最常用的属性。第一次加载 Hibernate 时会根据 Model 类自动建立表的结构(前提是先建立好数据库)。以后加载 Hibernate 时,会根据 Model 类自动更新表结构,即使表结构改变了,但表中的数据仍然存在,不会被删除。要注意的是,当部署到服务器后,表结构是不会被马上建立起来的,要等应用程序第一次运行起来后才会建立。Update 表示如果 Entity 实体的字段发生了变化,那么直接在数据库中进行更新。
validate 每次加载 Hibernate 时,会验证数据库的表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

4.了解 JPA 注解和属性

4.1 常用注解

注解 说明
@Entity 声明类为实体
@Table 声明表名,@Entity 和 @Table 注解一般一块使用,如果表名和实体类名相同,那么 @Table 可以省略
@Basic 指定非约束明确的各个字段
@Embedded 用于注释属性,表示该属性的类是嵌入类(@embeddable 用于注释 Java 类的,表示类是嵌入类)
@Id 指定的类的属性,一个表中的主键
@GeneratedValue 指定如何标识属性可以被初始化,如 @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="repair_seq"),表示主键生成策略是 sequence,还有 Auto、Identity、Native 等
@Transient 表示该属性并非一个数据库表的字段的映射,ORM 框架将忽略该属性。如果一个属性并非数据库表的字段映射,就务必将其标示为 @Transient,即它是不持久的,为虚拟字段
@Column 指定持久属性,即字段名。如果字段名与列名相同,则可以省略。使用方法,如 @Column(length=11, name="phone", nullable=false, columnDefinition="varchar(11) unique comment '电话号码'")
@SequenceGenerator 指定在 @GeneratedValue 注解中指定的属性的值。它创建一个序列
@TableGenerator 在数据库生成一张表来管理主键生成策略
@AccessType 这种类型的注释用于设置访问类型。如果设置 @AccessType(FIELD),则可以直接访问变量,并且不需要使用 Getter 和 Setter 方法,但必须为 public 属性。如果设置 @AccessType(PROPERTY),则通过 Getter 和 Setter 方法访问 Entity 的变量
@UniqueConstraint 指定的字段和用于主要或辅助表的唯一约束
@ColumnResult 可以参考使用 select 子句的 SQL 查询中的列名
@NamedQueries 指定命名查询的列表
@NamedQuery 指定使用静态名称的查询
@Basic 指定实体属性的加载方式,如 @Basic(fetch=FetchType.LAZY)
@Jsonignore 作用是 JSON 序列化时将 Java Bean 中的一些属性忽略掉,序列化和反序列化都受影响

4.2 映射关系的注解

注解 说明
@JoinColumn 指定一个实体组织或实体集合。用在 多对一一对多 的关联中
@OneToOne 定义表之间 一对一 的关系
@OneToMany 定义表之间 一对多 的关系
@ManyToOne 定义表之间 多对一 的关系
@ManyToMany 定义表之间 多对多 的关系

4.3 映射关系的属性

属性名 说明
targetEntity 表示默认关联的实体类型,默认为当前标注的实体类。
cascade 表示与此实体一对一关联的实体的级联样式类型,以及当对实体进行操作时的策略。在定义关系时经常会涉及是否定义 Cascade(级联处理)属性,如果担心级联处理容易造成负面影响,则可以不定义。它的类型包括 CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)、CascadeType.ALL(级联新建、更新、删除、刷新)。
fetch 该实体的加载方式,包含 LAZY 和 EAGER。
optional 表示关联的实体是否能够存在 null 值。默认为 true,表示可以存在 null 值。如果为 false,则要同时配合使用 @JoinColumn 标记。
mappedBy 双向关联实体时使用,标注在不保存关系的实体中。
JoinColumn 关联指定列。该属性值可接收多个 @JoinColumn。用于配置连接表中外键列的信息。@JoinColumn 配置的外键列参照当前实体对应表的主键列。
JoinTable 两张表通过中间的关联表建立联系时使用,即多对多关系。
PrimaryKeyJoinColumn 主键关联。在关联的两个实体中直接使用注解 @PrimaryKeyJoinColumn 注释。

懒加载 LAZY 和实时加载 EAGER 的目的是,实现关联数据的选择性加载。

  • 懒加载 是一种延迟加载策略,即在真正需要访问对象的属性时,才从数据库中加载数据。当 Hibernate 在查询数据库时遇到关联对象(如一对多、多对一、多对多等关系),并不会立即加载关联对象的数据,而只会加载主键 ID。只有当程序真正需要访问关联对象的属性时,Hibernate 才会发出 SQL 语句去加载数据。这种策略的优点在于,如果程序并不需要访问关联对象的所有属性,那么就可以节省数据库访问的开销,提高程序性能。但是,如果在使用懒加载的对象后关闭了 Session,那么在访问其关联对象的属性时,就会抛出 LazyInitializationException 异常,因为 Hibernate 需要在 Session 的作用范围内才能发出 SQL 语句加载数据。
  • 实时加载 是一种积极加载策略,即在加载一个对象时,会立即加载与其关联的所有对象。当 Hibernate 在查询数据库时遇到关联对象,会立即发出 SQL 语句加载关联对象的数据,并将其全部加载到内存中。这种策略的优点在于,程序可以随时访问关联对象的属性,而无需担心 Session 是否已经关闭。但是,如果关联对象的数据量很大,那么就会消耗大量的内存,并可能导致性能下降。

在 Spring Data JPA 中,要控制 Session 的生命周期,否则会出现 could not initialize proxy [xxxx#18] - no Session 错误。可以在配置文件中配置以下代码来控制 Session 的生命周期:

xml 复制代码
spring.jpa.open-in-view=true
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

5.用 JPA 构建实体数据表

下面通过实例来体验如何通过 JPA 构建对象/关系映射的实体模型。

java 复制代码
package com.example.demo.entity;

import lombok.Data;

import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

@Entity
@Data
public class Article implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false, unique = true)
    @NotEmpty(message = "标题不能为空")
    private String title;

    @Column(columnDefinition = "enum('图','图文','文')")
    private String type;

    private Boolean available = Boolean.FALSE;
    @Size(min = 0, max = 20)
    private String keyword;
    @Size(max = 255)
    private String description;
    @Column(nullable = false)
    private String body;

    @Transient
    private List keywordlists;

    public List getKeywordlists() {
        return Arrays.asList(this.keyword.trim().split("|"));
    }

    public void setKeywordlists(List keywordlists) {
        this.keywordlists = keywordlists;
    }
}

如果想创建虚拟字段,则通过在属性上加注解 @Transient 来解决。

运行项目后会自动生成数据表。完成后的数据表如下图所示。

相关推荐
Theodore_10222 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸3 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象3 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了4 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·4 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王4 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康4 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
qq_17448285755 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序
转世成为计算机大神5 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式