JPA 2定义了一个类型安全的Criteria API,它允许使用所谓的JPA Metamodel构建条件查询。引入此功能是为了避免上述缺点,并提供类型安全和静态的方式来访问实体类的元数据。请注意,元模型生成的任务可以自动化。JBoss、EclipseLink、OpenJPA、DataNucleus只是可用于元模型生成的一些工具。
在本文中,我们将了解如何将这些元模型生成器工具之一集成到 Spring Boot 和 Kotlin 项目中。
规范元模型
元模型是一组描述域模型的对象。
这个元模型在两个方面很重要。首先,它允许提供者和框架以通用方式处理应用程序的域模型。
其次,从应用程序编写者的角度来看,它允许非常流畅地表达完全类型安全的条件查询。— Hibernate 社区文档
JPA 2 (JSR 317) 规范中描述了元模型类的结构。如果您有兴趣详细了解元模型类的定义方式,我建议您首先阅读此文档页面。否则,请随意跳到下一章。
配置元模型生成工具
为了生成元模型类,我们将使用JBosshibernate-jpamodelgen
提供的元模型生成器工具。
首先,我们需要将hibernate-jpamodelgen
依赖项添加到build.gradle.kts文件中,以及kapt
:
plugins {
kotlin("kapt") version "1.3.72"
}
dependencies {
implementation ("org.hibernate:hibernate-jpamodelgen:5.4.12.Final")
kapt("org.hibernate:hibernate-jpamodelgen:5.4.12.Final")
}
_kapt_
是Kotlin 注解处理工具hibernate-jpamodelgen
,在构建时自动生成元模型类需要它。
JPA 元模型类
为了展示元模型类的样子,我们需要一个实体类。由于在 Kotlin 中定义 JPA 实体可能很棘手,因此我建议先阅读本文。
假设我们已经定义了Author
实体类,如下所示:
@Entity
open class Author {
@get:Id
@get:GeneratedValue
@get:Column(name = "id")
open var id: Int? = null
@get:Column(name = "name")
open var name: String? = null
@get:Column(name = "surname")
open var surname: String? = null
@get:Column(name = "birth_date")
@get:Temporal(TemporalType.DATE)
open var birthDate: Date? = null
@get:ManyToOne(fetch = FetchType.LAZY)
@get:JoinColumn(name = "country_id")
open var country: Country? = null
@get:ManyToMany(fetch = FetchType.LAZY)
@get:JoinTable(
name = "author_book",
joinColumns = [JoinColumn(name = "author_id")],
inverseJoinColumns = [JoinColumn(name = "book_id")]
)
open var books: MutableSet<Book> = HashSet()
}
默认情况下,对应的元模型类会被放置kapt
在build/generated/source/kapt/
与对应的实体类相同的包中。
元模型类将在构建时自动生成,并且与实体同名,并在末尾添加“_”。因此,为该类生成的元模型类Author
将Author_
如下所示:
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Author.class)
public abstract class Author_ {
public static volatile SingularAttribute<Author, Integer> id;
public static volatile SingularAttribute<Author, String> name;
public static volatile SingularAttribute<Author, String> surname;
public static volatile SingularAttribute<Author, Date> birthDate;
public static volatile SingularAttribute<Author, Country> country;
public static volatile SetAttribute<Author, Book> books;
public static final String ID = "id";
public static final String NAME = "name";
public static final String SURNAME = "surname";
public static final String BIRTH_DATE = "birthDate";
public static final String COUNTRY = "country";
public static final String BOOKS = "books";
}
正如您所知,生成的元模型类是一个 Java 类。这不是问题,因为Kotlin 与 Java 完全可互操作。
JPA 元模型的实际应用
由于 Criteria API 提供了接受String引用以及Attribute接口实现的重载方法,因此我们可以像使用属性的String引用一样使用生成的元模型类。让我们编写一个条件查询来获取所有名为“John”的作者。
使用的条件查询如下所示Author_
:
// entityManager设置代码 ...
val criteriaBuilder = entityManager.criteriaBuilder
val criteriaQuery = criteriaBuilder.createQuery(Author::class.java)
// 检索所有名为“John”的作者
val root = criteriaQuery.from(Author::class.java)
criteriaQuery
.select(root)
.where(
criteriaBuilder.equal(root.get(Author_.name), "John")
)
val query = entityManager.createQuery(criteriaQuery)
val resultList = query.resultList
这就是不使用元模型类的情况:
// entityManager设置代码 ...
val criteriaBuilder = entityManager.criteriaBuilder
val criteriaQuery = criteriaBuilder.createQuery(Author::class.java)
// 检索所有名为“John”的作者
val root = criteriaQuery.from(Author::class.java)
criteriaQuery
.select(root)
.where(
criteriaBuilder.equal(root.get<String>("name"), "John")
)
val query = entityManager.createQuery(criteriaQuery)
val resultList = query.resultList
主要区别在于如何检索实体属性名称。在第一个片段中,我们使用Author_.name
引用而不是传统的列名称。如果该属性发生更改,在第一个示例中我们将收到编译时错误,而在第二个示例中,将引发更危险的运行时错误。使用元模型类可以使代码更干净、更简单、更健壮。
JPA Metamodel 提供了一种类型安全的方式来定义条件查询。这使得未来的重构比通过字符串引用属性要容易得多,从而使代码对更改更加稳健。
许多不同的元模型生成器工具可以与注释处理器一起使用,以在构建时生成元模型类。这意味着实体属性的更改将自动反映在元模型类中,从而避免运行时错误。
部分文章内容可能来自互联网,如有侵权,请通过邮件联系
暂无评论内容