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 上獲得。