从零开始 Spring Boot 48:JPA Hibernate

news2024/11/25 10:32:54

从零开始 Spring Boot 48:JPA & Hibernate

spring boot

图源:简书 (jianshu.com)

对象关系映射(ORM)是将Java对象转换为数据库表的过程。换句话说,这允许我们在没有任何SQL的情况下与关系数据库进行交互。Java Persistence API(JPA)是一个定义如何在Java应用程序中持久化数据的规范。JPA的主要焦点是ORM层。

Hibernate是目前使用的最流行的Java ORM框架之一。它的第一个版本几乎是20年前的事了,现在仍然有优秀的社区支持和定期发布。此外,Hibernate是JPA规范的标准实现,它还具有一些特定于Hibernate的附加特性。

以上内容摘抄自Learn JPA & Hibernate | Baeldung。

本文将简单介绍如何在 Spring Boot 中使用 JPA 和 Hibernate。

当然,这是一个相当宏大的议题,所以本篇文章只做一个入门介绍和引导。

准备

要使用 JPA 和 Hibernate,需要添加spring-boot-starter-data-jpa依赖,此外还需要添加你所使用的数据库驱动,我这里使用的是Mysql

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>

spring-boot-starter-data-jpa中包含 JDBC 相关依赖,所以不用手动添加 JDBC 相关依赖。

自然的,你还需要添加数据库相关配置:

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.database=MYSQL
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

比较特别的是,这里还添加了 JPA 的相关配置(spring.jpa.xxx),这里使了一下配置:

  • spring.jpa.database,要操作的目标数据库(类型),默认情况下自动检测。也可以使用spring.jpa.database-platform属性进行设置。
  • spring.jpa.hibernate.ddl-auto,DDL模式。这实际上是hibernate.hbm2ddl.auto属性的快捷方式。当使用嵌入式数据库并且未检测到架构管理器时,默认为“create-drop”。否则,默认为“无”。
  • spring.jpa.show-sql,是否启用SQL语句的日志记录(在控制台打印相关SQL)。

使用 Hibernate 的 DDL 模式有以下几种:

  • vlidate,每次加载 Hibernate 时,验证数据库表结构,将表结构与本地 model 进行对比,但不会创建新表,也不会插入数据。
  • create,每次加载 Hibernate 时,删除本地 model 对应的表,并使用本地 model 重新生成表结构。
  • create-drop,加载 Hibernate 时根据本地 model 生成表结构,SessionFactory 关闭后删除生成的表结构。
  • update,加载 Hibernate 时,如果数据库中缺少 model 对应的表结构,创建,否则将对比表结构和 model,如果不同,将使用 update DDL 对表结构进行更新。

一般而言,对于持久型数据库(如MySQL),使用update模式,对于内存数据库(如H2),使用create-drop模式。

Entity

每一个数据库表,对应到一个实体类(Entity):

@Entity
public class Student {
}

这个实体类用@Entity注解标识,默认情况下实体名称为类名。

如果表名与实体名称不同,需要使用@Table注解:

@Entity
@Table(name = "USER_STUDENT")
public class Student {
	// ...
}

虽然这里使用的表名是大写(USER_STUDENT),但实际上 Hibernate 会将其转化为全小写(user_student)后用于数据库查询或 DDL 语句。

必须要为实体类指定一个主键以对应数据库表的主键:

public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    // ...
}

@Id表名字段是实体类的主键,@GeneratedValue指明主键的生成方式:

  • AUTO,持久层为特定数据库使用适当的策略生成主键。
  • UUID,持久层生成 RFC 4122 通用唯一标识作为主键。
  • IDENTITY,使用数据库标识列(自增主键)作为主键。
  • SEQUENCE,使用数据库序列(xxx_seq表)作为主键。
  • TABLE,使用基本数据库表生成唯一主键。

对于普通列对应的字段,使用@Column注解:

public class Student {
    // ...
    @Column(name = "NAME", length = 50, nullable = false, unique = false)
    private String name;
    @Column(name = "BIRTH_DAY", nullable = false)
    private LocalDate birthDay;
    // ...
}

比较特别的是,如果字段类型是旧的时间类型(比如java.util.Date),就需要使用@Temporal注解进行转换,详细可以阅读这篇文章Hibernate – Mapping Date and Time | Baeldung。

如果某个字段不需要映射到数据库表:

public class Student {
	// ...
    @Transient
    private Integer age;
}

序列化和反序列化时要排除的字段使用transient关键字声明,和这里的@Transient注解有着类似的作用和命名方式。

对于枚举字段,可以指定其用字面量存储还是顺序值:

public class Student {
    // ...
    @Enumerated(EnumType.ORDINAL)
    private Gender gender;
    // ...
}

创建好实体类后,Spring 启动时会自动扫描实体类,并按照配置中设置好的 DLL 模式(这里是update)来处理表结构。

除了上边这些 JPA 相关的注解,实体类往往还需要实现 Getter/Setter/hashCode/toString/equals等常用方法,这些都可以利用 Lombok 的相关注解完成创建,此处不再展示,感兴趣的可以查看完整示例。

Repository

JPA 的相关 API 通过 Repository 接口操作数据库(类似于MyBatis的Mapper),并且提供一些基础的功能性 Repository 供我们使用和扩展:

  • CrudRepository,提供基本的 CRUD 操作。
  • PagingAndSortingRepository,提供分页和排序操作。
  • ListCrudRepository,在CrudRepository基础上提供列表相关操作。
  • ListPagingAndSortingRepository,在PagingAndSortingRepository基础上提供列表相关操作。
  • JpaRepository,提供所有以上操作。

一般而言,只需要让自定义接口扩展JpaRepository即可:

@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
}

Service

Service 层可以依赖注入 Repository 后查询数据库:

@Service
public class StudentService {
    @Autowired
    private StudentRepository studentRepository;

    public List<Student> list(){
        return studentRepository.findAll();
    }
}

Tests

编写测试用例:

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@SpringJUnitWebConfig(classes = {JpaApplication.class})
@TestPropertySource("classpath:application.properties")
public class StudentServiceTests {
    @Autowired
    private StudentService studentService;
    @Autowired
    private StudentRepository studentRepository;
    private List<Student> students = List.of(
            new Student("icexmoon", LocalDate.of(1989, 10, 1), Gender.MALE),
            new Student("JackChen", LocalDate.of(1990, 5, 1), Gender.MALE),
            new Student("HanMeimei", LocalDate.of(1991, 6, 1), Gender.FEMALE));

    @BeforeEach
    void beforeEach() {
        studentRepository.deleteAll();
        for(var student: students){
            studentRepository.save(student);
        }
    }

    @Test
    void testList() {
        List<Student> students = studentService.list();
        Assertions.assertEquals(this.students, students);
    }

    @AfterEach
    void afterEach(){
        studentRepository.deleteAll();
    }
}

因为这里涉及数据库,所以利用@BeforeEach在每次运行测试用例前向表中添加测试数据,并在@AfterEach方法中清空表中的数据。这么做是为了让每个测试用例在运行前都有相同的测试数据环境。

当然,这么做比较繁琐,使用事务会让事情简单很多:

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@SpringJUnitWebConfig(classes = {JpaApplication.class})
@TestPropertySource("classpath:application.properties")
@Transactional
public class StudentServiceV2Tests {
    @Autowired
    private StudentService studentService;
    @Autowired
    private StudentRepository studentRepository;
    private final List<Student> students = List.of(
            new Student("icexmoon", LocalDate.of(1989, 10, 1), Gender.MALE),
            new Student("JackChen", LocalDate.of(1990, 5, 1), Gender.MALE),
            new Student("HanMeimei", LocalDate.of(1991, 6, 1), Gender.FEMALE));

    @BeforeEach
    void beforeEach() {
        studentRepository.deleteAll();
        studentRepository.saveAll(students);
    }

    @Test
    void testList() {
        List<Student> students = studentService.list();
        Assertions.assertEquals(this.students, students);
    }
}

@Transactional可以为当前的测试套件(Test Suite)开启事务支持,并且测试类中的每个测试用例(@Test)都将在事务中运行,且在执行完毕后自动执行事务回滚。

特别的,测试用例生命周期方法(@BeforeEach@AfterEach)同样会包括在测试用例事务中,因此在这里可以将清理和添加测试数据的步骤添加在@beforeEach方法中。

  • 相应的,测试套件(测试类)的生命周期方法(@BeforeAll@AfterAll)不会被包含在事务中。
  • 如果想让某个测试用例执行后不回滚,可以添加@Commit注解。

The End,谢谢阅读。

可以从这里获取本文的完整示例。

参考资料

  • Spring JUnit Jupiter Testing Annotations :: Spring Framework
  • Transaction Management :: Spring Framework
  • Context Management :: Spring Framework
  • TestTransaction (Spring Framework 6.0.10 API)
  • Programmatic Transactions in the TestContext Framework | Baeldung
  • Learn JPA & Hibernate | Baeldung
  • UUID如何保证唯一性? - 知乎 (zhihu.com)
  • Hibernate – Mapping Date and Time | Baeldung
  • 从零开始 Spring Boot 33:Null-safety - 红茶的个人站点 (icexmoon.cn)
  • 从零开始 Spring Boot 35:Lombok - 红茶的个人站点 (icexmoon.cn)
  • Spring Boot with Hibernate | Baeldung
  • Bootstrapping Hibernate 5 with Spring | Baeldung

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/684784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis入门(4)-list

redis中list数据会按照插入顺序进行排序&#xff0c;其底层是一个无头结点的双向链表&#xff0c;因此表头和表尾的操作性能较高&#xff0c;但中间元素操作性能较差。 1.lpush key element [element ] 从表头插入元素 lpush nosql redis hbase lpush nosql mongdb2.lrange…

信息熵、条件熵、信息增益

一、信息熵 其中&#xff1a; &#xff1a;样本属于第i个类别的概率 &#xff1a;总样本数 &#xff1a;集合 中属于第 个类别的样本个数 二、条件熵 其中&#xff1a; &#xff1a;属性 的取值个数 &#xff1a;选出属性 取值等于 的样本集合 三、信息增益 信息增…

安全防御 --- IPSec理论(03)

DPD 死亡对等体检测&#xff08;dead peer detection&#xff09;&#xff0c;检查对端IKE SA&#xff08;iskmp sa&#xff09;是否存在。当隧道出现异常&#xff0c;检测出异常重新发起协商&#xff0c;维持隧道。 作用&#xff1a;DPD解决隧道黑洞问题&#xff0c;用于检查…

Docker搭建Hadoop集群

目录 1.拉取centos镜像 2.基础镜像配置(基于centos构建hadoopbase镜像) 3.集群环境配置 1.创建3个容器 2.配置网络 3.配置主机和ip的映射关系 4.配置3个节点的免密登录 4.搭建hadoop集群 1.安装hadoop 2.修改配置文件 3.分发Hadoop及配置文件my_env.sh 5.启动集群 …

使用CLion创建Cmake项目,使用GoogleTest和GoogleMock对代码进行测试

文章目录 1、环境准备2、CLion创建项目3、编写测试用例4、复杂测试用例 1、环境准备 注意版本匹配&#xff0c;我本地是g 8.1.0 的&#xff0c;最开始装了GoogleTest最新版1.10.0结果发现不能用&#xff0c;又回去下载旧的版本。g 8.1.0 应该可以使用 Google Test 1.8.1 版本。…

HLW8032交流电参数模块采样原理讲解

目录 一、文章概述 二、采样说明 1.采样方式 1.1隔离采样 电流型电压互感器&#xff1a; 1.2非隔离采样 2.采样范围 3.采样精度 三、数据说明 四、HLW8032模块说明 一、文章概述 本博文主要讲解HLW8032交流电参数模块的采样原理以及设计说明&#xff0c;才疏学浅&am…

微服务系列文章之 SpringCloud面试

1、什么是 Spring Cloud&#xff1f; Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序&#xff0c;提供与外部系统的集成。Spring cloud Task&#xff0c;一个生命周期短暂的微服务框架&#xff0c;用于快速构建执行有限数据处理的应用程序。 2、使…

团体程序设计天梯赛-练习集L2篇⑦

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的普通人。 &#x1f3c0;个人主页&#xff1a;陈童学哦CSDN &#x1f4a1;所属专栏&#xff1a;PTA &#x1f381;希望各…

奇异值分解MVDR算法功率谱估计MATLAB完整程序分享

clear ;close all;clc; 产生输入信号 N 1024; %样本点数 snr[20 25 30]; %信噪比 n0:N-1; %数据轴 g100; %蒙特卡诺仿真次数 M14; %阶数 Pmvdr_szeros(3,1024); %存放MVDR谱 signal1 exp(1i*0.1…

Oracle-DG备库应用查询不到数据问题处理

背景: 一套准备上线的Oracle 11G RAC主备集群&#xff0c;应用报告说部分模块测试发现在备库查不到新插入的数据&#xff0c;而且问题发生的频率很高&#xff0c;需确认主备之间同步是否存在问题&#xff0c;此套主备之间同步采用SYNCAFFIRM模式 问题分析: 接到问题之后&#…

2022(二等奖)C2464植物保护管理系统

作品介绍 一、需求分析 1. 应用背景 森林是陆地生态系统的主体&#xff0c;是人类生存与发展的物质基础。以森林为主要经营对象的林业&#xff0c;不仅承担着生态建设的主要任务&#xff0c;而且承担着提供多种林产品的重大使命。进入21世纪&#xff0c;人类正在继农业文明和…

libevent(12)bufferevent的基础知识

一、bufferevent的基本概念 bufferevent 是 libevent 中的一个事件缓冲 IO&#xff0c;内部实现了基本 socket recv/send 操作 &#xff0c;用户只需要调用 bufferevent 的 API 即可实现数据的读写。 &#xff08;1&#xff09;缓冲区&#xff1a;每个 bufferevent 都有一个读…

Effective第三版 中英 | 避免使用终结方法和清理方法

文章目录 Effective第三版前言避免使用终结方法和清理方法&#xff08;Avoid finalizers and cleaners&#xff09;总结 Effective第三版 前言 大家好&#xff0c;这里是 Rocky 编程日记 &#xff0c;喜欢后端架构及中间件源码&#xff0c;目前正在阅读 effective-java 书籍。…

基于SpringBoot+vue的家乡特色推荐系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

【C语言督学训练营 第十五天】常用的查找算法----顺序查找、二分查找、二叉排序树

文章目录 前言一、顺序查找1,思想2.代码实战 二、二分查找1.思想2.代码实战 三、二叉排序树1.建树思想2.删除节点思想3.代码实战 考研真题实战 前言 本篇博客会介绍到数据结构中常用到的查找算法&#xff0c;合理的使用查找算法可以让我们很轻松找到自己想要的答案。本小节必须…

Java异常面试题

什么是Java异常机制 Java异常机制是java语言为我们提供一种异常处理机制&#xff0c;在java语言中&#xff0c;异常本身是一个类&#xff0c;产生异常就是创建异常对象并抛出这个异常对象&#xff0c;程序发生异常情况之后程序会抛出封装了错误信息的异常对象&#xff0c;程序…

python-线性规划

线性规划&#xff1a;定义&#xff1a;1 线性规划&#xff08;Linear programming,简称LP&#xff09;&#xff0c;是运筹学中研究较早、发展较快、应用广泛、方法较成熟的一个重要分支&#xff0c;是辅助人们进行科学管理的一种数学方法&#xff0c;是研究线性约束条件下线性…

WPF 零基础入门笔记(2):控件模板+数据模版

文章目录 文章合集地址WPF控价模版解决重复嵌套标签书写的问题实战 WPF数据绑定解决界面和业务数据沟通的问题 WPF数据模版数据模板解决数据的样式设置&#xff08;以CellTemplate为例&#xff09;数据模板和控件模板的区别ItemTemplate 元素模板ItemTemplate是用于绝大部分控件…

CAT1模块 EC800M HTTP使用总结记录

分享记录一下 CAT1 模块EC800 HTTP 协议使用流程 ...... by 矜辰所致目录 前言一、基础说明1.1 CAT1 与 4G1.2 EC800M 模块1.3 HTTP 二、开始使用2.1 硬件设计部分2.2 模块上电流程2.3 PDP 上下文2.3.1 什么是 SGSN 和 GGSN &#xff1f; 三、 HTTP 流程3.1 客户端3.1.1 PDP 上…

UFS 13 - Logical Unit Management

UFS 13 - Logical Unit Management 1 Introduction2 Logical Unit features3 Logical Unit Configuration 基于UFS 3.1 标准文档阐述 UFS 1-UFS架构简介1 UFS 2 -UFS架构简介2 UFS 3 - UFS RPMB UFS 4 - UFS Boot UFS 5 - UFS UIC Layer: MIPI M-PHY UFS 6 - UAP – SCSI Comma…