Spring-Data-Jpa

Spring Data JPA 异常 “IllegalArgumentException: Not a Managed Type”

1、概览 使用 Spring Data JPA 时,应用启动出现异常。大致如下: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerAdapter' ... Caused by: java.lang.IllegalArgumentException: Not a managed type: ...OurEntity at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:583) at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:85) ... 大意是说,一些 Bean 创建失败了,导致应用启动失败。 根异常是 IllegalArgumentException:“Not a managed type”,本文将带你了解出现这个异常的原因,以及如何解决该异常。 2、缺少 @Entity 注解 出现这种异常的一个可能原因是,忘记使用 @Entity 注解来标记实体。 2.1、重现问题 假设有以下实体类: public class EntityWithoutAnnotation { @Id private Long id; } 及其对应的 Spring Data JPA repository: public interface EntityWithoutAnnotationRepository extends JpaRepository<EntityWithoutAnnotation, Long> { } 最后是 Application 启动类,它会扫描上面定义的所有类: @SpringBootApplication public class EntityWithoutAnnotationApplication { } 尝试使用此 Application 来启动 Spring Context:

Spring Data JPA 实现 updateOrInsert(更新或保存)

1、简介 在应用开发中,执行 “更新或插入” 操作(也称为 “upsert”)的需要是很常见的。这个操作涉及将新记录存入数据库表中,如果记录不存在,则插入新记录;如果记录已经存在,则更新现有记录。 本文将带你了解使用 Spring Data JPA 执行 “更新或插入” 操作的不同方法。 2、实体类 定义 CreditCard 实体类用于演示: @Entity @Table(name="credit_card") public class CreditCard { @Id @GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "credit_card_id_seq") @SequenceGenerator(name = "credit_card_id_seq", sequenceName = "credit_card_id_seq", allocationSize = 1) private Long id; private String cardNumber; private String expiryDate; private Long customerId; // Get / Set 方法省略 } 3、实现 本文介绍三种不同的方法来实现 “更新或插入”。 3.1、使用 Repository 方法 使用从 CrudRepository 接口继承的 save(entity) 方法在 Repository 中编写一个带事务的 default 方法。

Spring Data JPA 出现异常后不回滚,继续处理事务

1、概览 JPA 中的事务机制是一个功能强大的工具,它通过提交所有更改或在出现异常时回滚更改来确保原子性和数据完整性。然而,在某些情况下,我们可能需要在遇到异常的情况下继续进行事务处理而不回滚数据更改。 2、出现异常后事务自动回滚 在事务中可能会出现两种主要的异常情况。 2.1、在 Service 层出现异常后回滚事务 我们可能遇到回滚(Rollback)的第一个地方是在 Service 层,外部异常可能会影响数据库更改。 让我们通过下面的示例更仔细地了解一下这种情况。首先,添加 InvoiceEntity,作为数据模型: @Entity @Table(uniqueConstraints = {@UniqueConstraint(columnNames = "serialNumber")}) public class InvoiceEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; // 自增 ID private String serialNumber; // 序列号 private String description; // 说明 // Getter / Setter } 如上,包含了一个自增 ID、一个需要在整个系统中唯一的序列号和一个说明。 现在,创建负责发票事务操作的 InvoiceService: @Service public class InvoiceService { @Autowired private InvoiceRepository repository; @Transactional public void saveInvoice(InvoiceEntity invoice) { repository.save(invoice); sendNotification(); } private void sendNotification() { throw new NotificationSendingException("Notification sending is failed"); } } 在 saveInvoice() 方法中,我们添加了事务性保存发票(invoice)和发送相关通知的逻辑。但是,在发送通知的过程中,会抛出异常:

Spring Data JPA 查询 JSOB 类型的列

1、简介 Spring Data JPA 为与关系数据库交互提供了强大的抽象层。然而,传统的关系表可能并不适合存储复杂的、半结构化的数据,如产品详细信息或用户偏好。这就是 JSONB 数据类型的用武之地。 本文将带你学习使用 Spring Data JPA 查询 JSONB 列的各种方法。 2、JSONB 列 JSONB(JavaScript Object Notation for Databases)是一种数据类型,专门用于在 PostgreSQL 等关系数据库中存储 JSON 数据。它允许我们在单列中使用键值对和嵌套对象来表示复杂的数据结构。通过 JPA Provider(如 Hibernate),Spring Data JPA 允许我们将这些 JSONB 列映射到实体类中的属性。 3、映射 JSONB 列 我们可以使用 @Column 注解的 columnDefinition 属性,在实体类中明确定义列类型: @Column(columnDefinition = "jsonb") private String attributes; 这种方法主要与 PostgreSQL 相关,因为 PostgreSQL 本身支持 jsonb 数据类型。通过在实体类的相应属性中添加此注解,我们就能为数据库提供所需列类型的提示。Spring Data JPA 通常会根据数据库列定义自动检测 jsonb 数据类型,因此在很多情况下,此注解是可选的。 4、设置项目依赖和测试数据 创建一个基本的 Spring Boot 项目,其中包含测试 JSONB 查询所需的依赖和测试数据。 4.1、项目设置 首先,在 Maven pom.xml 文件中添加必要的依赖:

Querydsl 和 JPA Criteria

1、概览 Querydsl 和 JPA Criteria 是在 Java 中构建类型安全查询的流行框架。它们都提供了以静态类型表达查询的方法,使编写与数据库交互的高效、可维护代码变得更容易。本文将从多个角度对它们进行比较。 2、设置 首先,设置依赖。在所有示例中,都使用 HyperSQL 数据库: <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.7.1</version> </dependency> 添加 maven-processor-plugin,使用 JPAMetaModelEntityProcessor 和 JPAAnnotationProcessor 为框架生成元数据。配置如下: <plugin> <groupId>org.bsc.maven</groupId> <artifactId>maven-processor-plugin</artifactId> <version>5.0</version> <executions> <execution> <id>process</id> <goals> <goal>process</goal> </goals> <phase>generate-sources</phase> <configuration> <processors> <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </processors> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <version>6.2.0.Final</version> </dependency> </dependencies> </plugin> <persistence-unit name="com.baeldung.querydsl.intro"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/> <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:test"/> <property name="hibernate.connection.username" value="sa"/> <property name="hibernate.connection.password" value=""/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.

Spring Data JPA 中的 Refresh 和 Fetch

1、简介 Java Persistence API(JPA)是连接 Java 对象和关系数据库的桥梁,允许我们无缝地持久化和检索数据。本文将会带你了解各种策略和技术,以便在 JPA 中进行保存操作后有效地刷新(Refresh)和获取(Fetch)实体。 2、了解 Spring Data JPA 中的实体管理 在 Spring Data JPA 中 ,实体管理围绕 JpaRepository 接口展开,该接口是与数据库交互的主要机制。通过继承了 CrudRepository 的 JpaRepository 接口 ,Spring Data JPA 为实体的持久化、检索、更新和删除提供了一套强大的方法。 此外, Spring 容器会将实体管理器(EntityManager) 自动注入这些 repository 接口。该组件是嵌入 Spring Data JPA 的 JPA 基础架构的一个组成部分,可促进与底层持久化上下文(Persistence Context)的交互和 JPA 查询的执行。 2.1、Persistence Context JPA 中的一个关键组件是持久化上下文(Persistence Context)。把这个上下文想象成一个临时存放区,JPA 在这里管理检索或创建实体的状态。 它可以确保: 实体是唯一的: 在任何时候,上下文中都只存在一个具有特定主键的实体实例。 跟踪更改: 实体管理器(EntityManager)会跟踪上下文中对实体属性所做的任何修改。 保持数据一致性: 在事务处理期间,实体管理器会将上下文中的更改与底层数据库同步。 2.2、JPA 实体的生命周期 JPA 实体有四个不同的生命周期阶段: 瞬时(New)、托管(Managed)、删除(Removed)和游离(Detached)。 当我们使用实体的构造函数创建一个新实体实例时,它处于 “瞬时” 状态。我们可以通过检查实体的 ID(主键)是否为 null 来验证这一点: Order order = new Order(); if (order.

Spring Data 3 中的新 CRUD Repository 接口

1、概览 本文将带你了解 Spring Data 3 中引入的新 Repository 接口。 Spring Data 3 引入了基于 List 的 CRUD Repository 接口,可用于替换返回 Iterable 的现有 CRUD Repository 接口。此外,分页和排序接口默认不继承原始 CRUD Repository,而是由用户自己选择。接下来看看这些接口与现有接口有何不同,以及如何使用它们。 2、项目设置 首先创建一个 Spring Boot 应用,其中包含一个简单的实体和将使用新接口的 Repository。 2.1、依赖 首先,为项目添加所需的 spring-boot-starter-data-jpa 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>3.2.3</version> </dependency> 除此之外,还要配置数据库。可以使用任何 SQL 数据库,本文将使用 H2 内存数据库: <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> 2.2、Entity 创建一个 Book 实体: @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; private String author; private String isbn; // 构造器、Getter、Setter } 有了实体后,来看看如何使用新接口与数据库交互。

使用 JUnit 和 @DataJpaTest 测试 Spring Data Repository

1、简介 在使用 Spring Data JPA 进行数据持久化的 Spring Boot 应用中,测试与数据库交互的 Repository 至关重要。 本文将带你了解如何使用 Spring Boot 提供的 @DataJpaTest 注解和 JUnit 对 Spring Data JPA Repository 进行有效地测试。 2、了解 @DataJpaTest 和 Repository 类 本节主要介绍在 Spring Boot 应用中,@DataJpaTest 和 Repository 之间的交互。 2.1、@DataJpaTest @DataJpaTest 注解用于测试 Spring Boot 应用中的 JPA Repository。它是一个专门的测试注解,为测试持久层提供了一个最小的 Spring Context。该注解可与 @RunWith 和 @SpringBootTest 等其他测试注解结合使用。 此外,@DataJpaTest 的范围仅限于应用的 JPA Repository 层。它不会加载整个 Application Context,从而使测试更快、更集中。此注解还为测试 JPA 实体提供了预配置的 EntityManager 和 TestEntityManager。 2.2、Repository 在 Spring Data JPA 中,Repository 是 JPA 实体之上的一个抽象层。它为执行 CRUD(创建、读取、更新、删除)操作和执行自定义查询提供了一组方法。这些 Repository 通常从 JpaRepository 等接口继承而来,负责处理与特定实体类型相关的数据库交互。

Spring Data JPA 执行 INSERT 时跳过 SELECT

1、概览 在某些情况下,当使用 Spring Data JPA Repository 保存实体时,可能会在日志中遇到额外的 SELECT。这可能会因大量额外调用而导致性能问题。 本文将带你了解如何在 Spring Data JPA 中执行 INSERT 时跳过 SELECT,以提高性能。 2、设置 在深入 Spring Data JPA 并对其进行测试之前,先要做一些准备工作。 2.1、依赖 为了创建测试 Repository,需要使用 Spring Data JPA 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> 使用 H2 数据库作为测试数据库: <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> 使用 Spring Context 进行集成测试。添加 spring-boot-starter-test 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> 2.2、配置 本例中使用的 JPA 配置如下: spring.jpa.hibernate.dialect=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.hibernate.show_sql=true spring.jpa.hibernate.hbm2ddl.auto=create-drop 如上,让 Hibernate 生成 schema,并将所有 SQL 查询记录到日志中。 3、导致 SELECT 查询的原因 首先,创建一个 Task 实体:

Spring Data JPA Repository 和数据库视图

1、概览 数据库视图(Database View)是关系型数据库系统中的一种类似表的结构,其中的数据源来自一个或多个连接在一起的表。 Spring Data Repository 通常用于数据库表,但也可以有效地应用于数据库视图。本文将带你了解如何在 Spring Data JPA 中使用 Repository 从数据库视图检索数据。 2、数据库表设置 本文使用 H2 数据库系统进行数据定义,并使用 SHOP 和 SHOP_TRANSACTION 这两个示例表演示数据库视图概念。 SHOP 表存储商店信息: CREATE TABLE SHOP ( shop_id int AUTO_INCREMENT, shop_location varchar(100) NOT NULL UNIQUE, PRIMARY KEY(shop_id) ); SHOP_TRANSACTION 表存储与商店相关的交易记录,并通过 shop_id 对 SHOP 表进行引用: CREATE TABLE SHOP_TRANSACTION ( transaction_id bigint AUTO_INCREMENT, transaction_date date NOT NULL, shop_id int NOT NULL, amount decimal(8,2) NOT NULL, PRIMARY KEY(transaction_id), FOREIGN KEY(shop_id) REFERENCES SHOP(shop_id) ); 在实体-关系(ER)模型中,可以将其说明为 “一对多” 的关系,即一个商店可以有多笔交易。但是,每笔交易只与一家商店相关联。可以用 ER 图直观地表示这一点: