Jpa

解决 JPA 中的 PostgreSQL JSON 类型不匹配异常

1、简介 本文将带你了解在使用 JPA 与 PostgreSQL 交互时出现异常 “PSQLException error: column is of type json but the expression is of type character varying” 的原因,以及解决办法。 2、常见的原因 在 PostgreSQL 中,JSON 或 JSONB 数据类型用于存储 JSON 数据。但是,如果我们试图将字符串(character varyin)插入到期望使用 JSON 的列中,PostgreSQL 就会抛出 “column is of type json but expression is of type character varying” 错误。这种情况在使用 JPA 和 PostgreSQL 时尤其常见,因为 JPA 可能会尝试将字符串保存到 JSON 列,从而导致此错误。 3、异常演示 创建一个基本的 Spring Boot 项目,在 Maven pom.xml 文件中添加 PostgreSQL 依赖: <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.7.1</version> <scope>runtime</scope> </dependency> 创建一个映射到 student 表的 JPA 实体类:

Java 枚举、JPA 和 PostgreSQL 枚举

1、简介 本文将带你了解 Java 枚举、JPA 和 PostgreSQL 枚举的概念,以及如何将它们结合使用,在 Java 枚举和 PostgreSQL 枚举之间创建无缝映射。 2、Java 枚举 Java 枚举(Enum)是一种特殊类型的类,用于表示一组固定数量的常量。枚举用于定义一组具有底层类型(如字符串或整数)的命名值。当我们需要定义一组在应用中具有特定含义的命名值时,枚举非常有用。 下面是一个 Java 枚举的示例: public enum OrderStatus { PENDING, IN_PROGRESS, COMPLETED, CANCELED } 在本例中,OrderStatus 枚举定义了四个常量。这些常量可以在我们的应用中用来表示订单的状态。 3、使用 @Enumerated 注解 在 JPA 中使用 Java 枚举时,需要用 @Enumerated 来注解枚举字段,以指定如何将枚举值存储到数据库中。 首先,定义一个名为 CustomerOrder 的实体类,并用 @Entity 进行注解,以标记其用于 JPA 持久化: @Entity public class CustomerOrder { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Enumerated() private OrderStatus status; // 其他的字段和方法省略 } 默认情况下,JPA 将枚举值存储为整数(Integer),代表枚举常量的序号位置(ordinal())。例如,上文中的 OrderStatus 枚举值 PENDING、IN_PROGRESS、COMPLETED 和 CANCELED,默认行为将分别把它们存储为整数 0、1、2 和 3。由此产生的数据库表将有一个小 int 类型的状态列,其值为 0 至 3:

在 JPA 中使用 CriteriaQuery 执行 COUNT 查询

1、简介 Java Persistence API(JPA)是一种广泛使用的规范,用于访问、持久化和管理 Java 对象与关系数据库之间的数据。JPA 应用中的一项常见任务是计算符合特定条件的实体数量。使用 JPA 提供的 CriteriaQuery API 可以高效地完成这项任务。 CriteriaQuery 的核心组件是 CriteriaBuilder 和 CriteriaQuery 接口。CriteriaBuilder 是创建各种查询元素(如 Predicate、表达式和 CriteriaQuery)的工厂。而,CriteriaQuery 代表一个查询对象,它封装了 select、filter 和 order 标准。 本文将带你了解 JPA 中的 COUNT 查询,学习如何利用 CriteriaQuery API 轻松高效地执行 COUNT 操作。 本文以一个简单的图书管理系统为例,介绍如何利用 CriteriaQuery API 生成各种场景下的图书 COUNT 查询。 2、依赖 创建示例项目。 添加所需的 maven 依赖,包括 spring-data-jpa、spring-boot-starter-test 和 h2 内存数据库: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> 依赖添加完成后,创建图书管理系统示例。它允许我们执行各种查询,如统计所有图书,统计某个作者、书名和年份的图书的各种组合。 添加一个 Book (图书)实体,包含了 title、author、category 和 year 字段:

清除 JPA/Hibernate 中托管的实体

1、概览 本文将带你了解 JPA 是如何托管实体的,以及 Persistence Context(持久化上下文)由于外部变化而无法返回最新数据的情况。 2、Persistence Context 每个 EntityManager 都与一个 Persistence Context 相关联,该上下文在内存中存储所管理的实体。每当通过 EntityManager 对实体执行任何数据操作时,该实体就会变成由 Persistence Context 管理的实体。 当再次检索实体时,JPA 会从 Persistence Context 返回托管实体,而不是从数据库中获取。这种缓存机制有助于提高性能,而无需从数据库中重复获取相同的数据。 Persistence Context 在 JPA 中也被称为一级(first-level,L1)缓存。 3、Demo 设置 首先,创建一个简单的实体类: @Entity @Table(name = "person") public class Person { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; // 构造函数、Getter、Setter 方法省略 } 接下来,创建一个 Person 实体并将其持久化到数据库中: EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); Person person = new Person(); person.setName("David Jones"); entityManager.persist(person); transaction.

JPA 中实体的继承与组合

1、简介 继承(Inheritance)和组合(Composition)是面向对象编程(OOP)中的两个基本概念,我们也可以在 JPA 中利用它们进行数据建模。在 JPA 中,继承和组合都是对实体间关系进行建模的技术,但它们代表的是不同类型的关系。本文将带你了解这两种方法及其影响。 2、JPA 中的继承 继承是一种 “is-a” 关系,即子类继承超类的属性和行为。这允许子类从超类继承属性和方法,从而促进了代码的重用。JPA 提供了几种策略来模拟实体与其对应的数据库表之间的继承关系。 2.1、单表继承(STI) 单表继承(Single Table Inheritance,STI)将所有子类映射到单个数据库表中。通过利用 区分列 来区分子类实例,这简化了 Schema 管理和查询执行过程。 首先,使用 @Entity 注解将 Employee 实体类定义为超类。接下来,将继承策略设置为 InheritanceType.SINGLE_TABLE,这样所有子类都会映射到同一个数据库表*。 然后,使用 @DiscriminatorColumn 注解来指定 Employee 类中的 区分列。该列用于区分单个表中不同类型的实体。 示例如下,使用 name = "employee_type" 将列名称指定为 employee_type,并使用 discriminatorType = DiscriminatorType.STRING 表示列包含字符串值: @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "employee_type", discriminatorType = DiscriminatorType.STRING) public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // Get / Set 方法省略 } 对于每个子类,使用 @DiscriminatorValue 注解来指定与该子类相对应的 区别列 的值。在本例中,我们使用 manager 和 developer 分别作为 Manager 和 Developer 子类的 区别值:

JPA 中的 PersistenceUnit 和 PersistenceContext

1、概览 Persistence Context(持久化上下文)和 Persistence Unit(持久化单元)是 JPA 中的两个重要概念,用来管理应用中实体的生命周期。 本文将带你了解 JPA 中的 EntityManager(实体管理器) 的作用,以及 Persistence Context 和 Persistence Unit 的重要性和用例。 2、EntityManager 和 EntityManagerFactory 首先来看看 EntityManager 和 EntityManagerFactory 接口,它们在管理持久性(Persistence)、实体和数据库交互方面发挥着重要作用。 2.1、EntityManager EntityManager 是一个与 Persistence Context 交互的接口。它对实体执行 CRUD 操作、跟踪更改并确保在事务提交时与数据库同步。EntityManager 代表一个 Persistence Context,并在事务范围内运行。 2.2、EntityManagerFactory EntityManagerFactory 是一个创建 EntityManager 的接口,有效地发挥着工厂的作用。创建时,EntityManagerFactory 会与特定的 Persistence Unit 关联,从而创建 EntityManager 的实例。 3、PersistenceContext PersistenceContext 是一个短暂的、事务范围的上下文,用于管理实体的生命周期。它代表一组存储在内存中的 “托管实体”,是实体管理器的一级缓存。如果事务开始,就会创建持久化上下文,并最终在事务提交或回滚时关闭或清除。 持久化上下文会自动检测对托管实体所做的更改,并确保所有实体更改与持久化存储(Persistence Storage)同步。 我们可以使用 @PersistenceContext 注解定义持久化上下文(Persistence Context)的类型: @PersistenceContext private EntityManager entityManager; JPA 中有两种持久化上下文: TRANSACTION 和 EXTENDED。 首先,使用 @Entity 注解创建与 PRODUCT 表相对应的实体:

REST 查询语言 - 实现 OR 操作

1、概览 本文将扩展 上一篇文章 中实现的高级搜索操作,在 REST API 查询语言中加入基于 OR 的搜索条件。 2、实现方法 以前,search 查询参数中的所有条件都是由 AND 运算符组成的条件。 现在,使用标志来指示必须使用 OR 运算符组合条件。 http://localhost:8080/users?search=firstName:john,'lastName:doe 注意,这里用单引号标记了条件 lastName,以示区别。我们会在条件值对象 SpecSearchCriteria 中捕获 OR 运算符的 Predicate: public SpecSearchCriteria( String orPredicate, String key, SearchOperation operation, Object value) { super(); this.orPredicate = orPredicate != null && orPredicate.equals(SearchOperation.OR_PREDICATE_FLAG); this.key = key; this.operation = operation; this.value = value; } 3、改进 UserSpecificationBuilder 修改 UserSpecificationBuilder,以在构建 Specification<User> 时考虑 OR 条件: public Specification<User> build() { if (params.size() == 0) { return null; } Specification<User> result = new UserSpecification(params.

REST 查询语言 - 高级搜索操作

1、概览 前面几篇文章介绍了三种 REST 查询语言的实现方式,分别是:JPA Criteria、Spring Data JPA Specification 和 QueryDSL。 本文将扩展前面几篇文章中开发的 REST 查询语言,使其包含更多搜索操作,如:等于、不等于、大于、小于、前缀、后缀、包含和类似(Like)。 本文选择使用 Specification,因为它是一种清晰而灵活的表示操作的方式。 2、SearchOperation 枚举 首先,通过枚举来更好地定义各种支持的搜索操作: public enum SearchOperation { EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS; public static final String[] SIMPLE_OPERATION_SET = { ":", "!", ">", "<", "~" }; public static SearchOperation getSimpleOperation(char input) { switch (input) { case ':': return EQUALITY; case '!': return NEGATION; case '>': return GREATER_THAN; case '<': return LESS_THAN; case '~': return LIKE; default: return null; } } } 有两组操作:

在 JPA 投影查询中使用 Record

Java 16 中引入的 Java Record 允许轻松地定义数据类(Data Class),这非常适合用于 JPA 中的投影查询。 Record 不能作为实体类 Record 只能用于投影查询。像 Hibernate 等流行的 JPA 实现创建代理对象时需要无参构造函数、非 final 字段、setter 方法和非 final 的实体类。而这些特性在 Record 中要么被不鼓励使用,要么被明确禁止使用。 Record 和 JPA 如果你在应用中直接使用 JPA,有几种不同的方法可以将记 Record 整合到 DAO 层中。 CriteriaBuilder Record 可与 CriteriaBuilder 一起使用,如下: public List<AdvocateRecord> findAllWithCriteriaBuilder() { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<AdvocateRecord> cq = cb.createQuery(AdvocateRecord.class); Root<AdvocateEntity> root = cq.from(AdvocateEntity.class); cq.select(cb.construct( AdvocateRecord.class, root.get("id"), root.get("fName"), root.get("lName"), root.get("region"), root.get("twitterFollowers"))); TypedQuery<AdvocateRecord> q = em.createQuery(cq); return q.getResultList(); } TypedQuery Record 也可以与 TypedQuery 一起使用,但需要在 JPQL 查询中提供完整类路径的构造函数。

在 JPA 和 Sping Data JPA 中使用 Java Record

1、概览 在本教程中,我们将探讨如何在 JPA 中使用 Java Record,包括以下内容。 为什么 Record 不能作为 Entity 使用。 在 JPA 中使用 Record。 在 Spring Boot 应用中使用 Spring Data JPA 和 Record。 2、Record 和 Enttiy Record 是不可变的,用于存储数据。它们包含字段、全参数构造函数、getter、toString 和 equals/hashCode 方法。由于它们是不可变的,因此没有 setter。由于其语法简洁,在 Java 中经常被用作数据传输对象(DTO)。 Entity(实体)是映射到数据库表的类。它们用于表示数据库中的条目。它们的字段被映射到数据库表中的列。 2.1、Record 不能作为 Entity 实体由 JPA provider 处理。JPA provider 负责创建数据库表,将实体映射到表,并将实体持久化到数据库。在流行的 JPA provider(如 Hibernate)中,实体是使用代理来创建和管理的。 代理是在运行时生成并继承实体类的类。这些代理依赖于实体类的无参数构造函数和 setter。由于 Record 不具有这些,所以它们不能用作实体。 2.2、在 JPA 中使用 Record 的其他方法 由于在 Java 中使用 Record 的简便性和安全性,在 JPA 中以其他方式使用 Record 可能是有益的。 在 JPA中,我们可以通过以下方式使用 Record: 将查询结果转换为 Record。 使用 Record 作为DTO在各个层之间传输数据。 将实体转换为 Record。 3、项目设置 我们将使用 Spring Boot 创建一个使用 JPA 和 Spring Data JPA 的简单应用程序。然后,我们将了解在与数据库交互时使用 Record 的几种方法。