Hibernate 6 中的布爾轉換器
一、概述
在本教程中,我們將學習如何使用 Hibernate 6 中添加的boolean
轉換器來映射域模型中的boolean
屬性。 Hibernate 6 改進了類型系統。因此,更新刪除了一些用於表示boolean
值的現有類。
2.型號
為了說明如何在 Hibernate 6 中使用boolean
轉換器,我們將在測試中使用 H2 數據庫。首先,我們將使用 SQL 腳本創建一個數據庫表:
CREATE TABLE Question (
id UUID,
content VARCHAR,
correctAnswer CHAR,
shouldBeAsked CHAR,
isEasy TINYINT,
wasAskedBefore CHAR,
PRIMARY KEY (id)
)
然後,在 Java 中,我們將把這個表映射到Question
實體類:
@Entity
public class Question {
@Id
private UUID id;
private String content;
private Boolean correctAnswer;
private Boolean shouldBeAsked;
private Boolean isEasy;
private Boolean wasAskedBefore;
public Question() {
}
// standard setters and getters
}
3. 使用YesNoConverter
映射Y
和N
通常,大寫字母Y
和N
分別用於表示true
值或false
值。以前,我們可以通過使用@Type
註釋字段並指定YesNoBooleanType
來映射它們:
`// old way
@Type(type = "org.hibernate.type.YesNoBooleanType")
private Boolean someBoolean;
Hibernate 6 將YesNoBooleanType
替換為YesNoConverter
。**因為它是一個轉換器,所以我們必須將它與@Convert
而不是@Type
一起使用。**
讓我們將這個新轉換器應用於Question
中的correctAnswer
:
@Convert(converter = YesNoConverter.class)
private Boolean correctAnswer;
現在,讓我們驗證Y
和N
是否正確映射:
@Test
void whenFieldAnnotatedWithYesNoConverter_ThenConversionWorks() {
session.beginTransaction();
UUID likeJavaQuestionId = UUID.randomUUID();
UUID sydneyCapitalOfAustraliaQuestionId = UUID.randomUUID();
session.persist(new QuestionBuilder().id(likeJavaQuestionId)
.content("Do you like Java?")
.correctAnswer(true)
.build());
session.persist(new QuestionBuilder().id(sydneyCapitalOfAustraliaQuestionId)
.content("Is Sydney the capital of Australia?")
.correctAnswer(false)
.build());
session.flush();
char likeJavaQuestionCorrectAnswerDbValue = session.createNativeQuery(format("SELECT correctAnswer FROM Question WHERE id='%s'", likeJavaQuestionId), Character.class)
.getSingleResult();
char sydneyCapitalOfAustraliaQuestionCorrectAnswerDbValue = session.createNativeQuery(format("SELECT correctAnswer FROM Question WHERE id='%s'", sydneyCapitalOfAustraliaQuestionId), Character.class)
.getSingleResult();
session.close();
assertEquals('Y', likeJavaQuestionCorrectAnswerDbValue);
assertEquals('N', sydneyCapitalOfAustraliaQuestionCorrectAnswerDbValue);
}
4. 使用TrueFalseConverter
映射T
和F
同樣,我們可以使用大寫字母T
和F
來表示一個boolean
。 Hibernate 6 刪除了TrueFalseBooleanType
:
// old way
@Type(type = "org.hibernate.type.TrueFalseBooleanType")
private Boolean someBoolean;
作為替代,我們將使用TrueFalseConverter
來指定此映射。讓我們為shouldBeAsked
註冊它:
@Convert(converter = TrueFalseConverter.class)
private Boolean shouldBeAsked;
讓我們測試轉換:
@Test
void whenFieldAnnotatedWithTrueFalseConverter_ThenConversionWorks() {
session.beginTransaction();
UUID codeTestedQuestionId = UUID.randomUUID();
UUID earningsQuestionId = UUID.randomUUID();
session.persist(new QuestionBuilder().id(codeTestedQuestionId)
.content("Is this code tested?")
.shouldBeAsked(true)
.build());
session.persist(new QuestionBuilder().id(earningsQuestionId)
.content("How much do you earn?")
.shouldBeAsked(false)
.build());
session.flush();
char codeTestedQuestionShouldBeAskedDbValue = session.createNativeQuery(format("SELECT shouldBeAsked FROM Question WHERE id='%s'", codeTestedQuestionId), Character.class)
.getSingleResult();
char earningsQuestionsShouldBeAskedDbValue = session.createNativeQuery(format("SELECT shouldBeAsked FROM Question WHERE id='%s'", earningsQuestionId), Character.class)
.getSingleResult();
session.close();
assertEquals('T', codeTestedQuestionShouldBeAskedDbValue);
assertEquals('F', earningsQuestionsShouldBeAskedDbValue);
}
5. 使用NumericBooleanConverter
映射0
和1
最後,字母不是表示boolean
唯一方式。為此,人們通常也使用整數0
和1
。過去,我們會使用NumericBooleanType
來表示:
// old way
@Type(type = "org.hibernate.type.NumericBooleanType")
private Boolean someBoolean;
讓我們改用新的NumericBooleanConverter
。我們會將零個或一個值映射到isEasy
:
@Convert(converter = NumericBooleanConverter.class)
private Boolean isEasy;
像往常一樣,讓我們檢查它是否按預期工作:
@Test
void whenFieldAnnotatedWithNumericBooleanConverter_ThenConversionWorks() {
session.beginTransaction();
UUID earthFlatQuestionId = UUID.randomUUID();
UUID shouldLearnProgrammingQuestionId = UUID.randomUUID();
session.persist(new QuestionBuilder().id(earthFlatQuestionId)
.content("Is the Earth flat?")
.isEasy(true)
.build());
session.persist(new QuestionBuilder().id(shouldLearnProgrammingQuestionId)
.content("Should one learn programming")
.isEasy(false)
.build());
session.flush();
int earthFlatQuestionIsEasyDbValue = session.createNativeQuery(format("SELECT isEasy FROM Question WHERE id='%s'", earthFlatQuestionId), Integer.class)
.getSingleResult();
int shouldLearnProgrammingQuestionIsEasyDbValue = session.createNativeQuery(format("SELECT isEasy FROM Question WHERE id='%s'", shouldLearnProgrammingQuestionId), Integer.class)
.getSingleResult();
session.close();
assertEquals(1, earthFlatQuestionIsEasyDbValue);
assertEquals(0, shouldLearnProgrammingQuestionIsEasyDbValue);
}
6. 使用@ConverterRegistration
指定默認轉換器
用轉換器註釋每個boolean
字段是乏味且容易出錯的。通常,我們在數據庫中僅以一種方式表示boolean
。
不出所料, Hibernate 提供的boolean
轉換器默認情況下並未設置為自動應用。但是,Hibernate 6.1 引入了@ConverterRegistration
,我們可以使用它來自動應用現有的轉換器。
為此,讓我們在我們的Question
類所在的包中添加一個帶有此註釋的package-info.java
文件,並默認應用YesNoConverter
:
@ConverterRegistration(converter = YesNoConverter.class)
package com.baeldung.hibernate.booleanconverters.model;
import org.hibernate.annotations.ConverterRegistration;
import org.hibernate.type.YesNoConverter;
我們沒有在@ConverterRegistration
中設置autoApply
,因為它默認設置為true
。 Question
實體包含boolean
字段wasAskedBefore
,並且由於我們沒有使用任何轉換器對其進行註釋,Hibernate 使用YesNoConverter
來映射它:
@Test
void givenConverterRegisteredToAutoApply_whenFieldIsNotAnnotated_ThenConversionWorks() {
session.beginTransaction();
UUID likeJavaQuestionId = UUID.randomUUID();
UUID likeKotlinQuestionId = UUID.randomUUID();
session.persist(new QuestionBuilder().id(likeJavaQuestionId)
.content("Do you like Java?")
.wasAskedBefore(true)
.build());
session.persist(new QuestionBuilder().id(likeKotlinQuestionId)
.content("Do you like Kotlin?")
.wasAskedBefore(false)
.build());
session.flush();
char likeJavaQuestionWasAskedBeforeDbValue = session.createNativeQuery(format("SELECT wasAskedBefore FROM Question WHERE id='%s'", likeJavaQuestionId), Character.class)
.getSingleResult();
char likeKotlinQuestionWasAskedBeforeDbValue = session.createNativeQuery(format("SELECT wasAskedBefore FROM Question WHERE id='%s'", likeKotlinQuestionId), Character.class)
.getSingleResult();
session.close();
assertEquals('Y', likeJavaQuestionWasAskedBeforeDbValue);
assertEquals('N', likeKotlinQuestionWasAskedBeforeDbValue);
}
7. 注意事項
我們可能會開始使用這些新的轉換器並發現 Hibernate 沒有正確填充我們的boolean
字段。這可能是因為**YesNoConverter
和TrueFalseConverter
只適用於大寫字符。小寫字母將被靜默映射到null
** :
@Test
void givenFieldAnnotatedWithYesNoConverter_WhenDbValueIsLowercase_ThenDomainModelValueNull() {
session.beginTransaction();
UUID mappedToNullQuestionId = UUID.randomUUID();
UUID behaviorIntuitiveQuestionId = UUID.randomUUID();
session.createNativeMutationQuery(format("INSERT INTO Question (id, content, correctAnswer) VALUES ('%s', 'Will correctAnswer be mapped to null?', 'y')", mappedToNullQuestionId))
.executeUpdate();
session.createNativeMutationQuery(format("INSERT INTO Question (id, content, correctAnswer) VALUES ('%s', 'Is this behavior intuitive?', 'n')", behaviorIntuitiveQuestionId))
.executeUpdate();
Question behaviorIntuitiveQuestion = session.get(Question.class, behaviorIntuitiveQuestionId);
Question mappedToNullQuestion = session.get(Question.class, mappedToNullQuestionId);
session.close();
assertNull(behaviorIntuitiveQuestion.getCorrectAnswer());
assertNull(mappedToNullQuestion.getCorrectAnswer());
}
8. 使用 JPA AttributeConverter
映射不同的表示
我們可以在數據庫中遇到各種boolean
表示。如果沒有一個默認的 Hibernate 轉換器滿足我們的要求,我們必須自己定義映射邏輯。我們可以通過創建 JPA 屬性轉換器輕鬆地做到這一點。
9.結論
在本文中,我們學習瞭如何使用 Hibernate 6 映射boolean
。
標準boolean
類型被轉換器取代。因此,我們需要使用@Convert
而不是@Type
。
此外,我們必須記住**YesNoConverter
和TrueFalseConverter
不適用於小寫字母。而且,為了減少樣板代碼,我們應該使用ConverterRegistration
來自動應用轉換器**。
我們看到的 Hibernate 轉換器都是 JPA AttributeConverter
的實現。如果都不符合我們的需要,我們可以自己寫一個實現,用同樣的方式使用。
與往常一樣,本文的完整代碼示例可在 GitHub 上獲得。