BAEL-6319 Hibernate 6 Boolean Converters (#13894)

Co-authored-by: Mariusz Kaczmarczyk <to@mariu.sh>
This commit is contained in:
mariudotsh 2023-04-30 04:34:19 +02:00 committed by GitHub
parent 5ba07d6188
commit 141df4e0ff
28 changed files with 400 additions and 81 deletions

View File

@ -1,4 +1,6 @@
package com.baeldung;
package com.baeldung.hibernate;
import static org.hibernate.boot.registry.StandardServiceRegistryBuilder.DEFAULT_CFG_RESOURCE_NAME;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -7,19 +9,20 @@ import org.hibernate.service.ServiceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.baeldung.manytomany.model.Employee;
import com.baeldung.manytomany.model.Project;
import com.baeldung.uuids.WebSiteUser;
import com.baeldung.uuids.Element;
import com.baeldung.uuids.Reservation;
import com.baeldung.uuids.Sale;
import com.baeldung.hibernate.booleanconverters.model.Question;
import com.baeldung.hibernate.manytomany.model.Employee;
import com.baeldung.hibernate.manytomany.model.Project;
import com.baeldung.hibernate.uuids.WebSiteUser;
import com.baeldung.hibernate.uuids.Element;
import com.baeldung.hibernate.uuids.Reservation;
import com.baeldung.hibernate.uuids.Sale;
public class HibernateUtil {
private static final String DEFAULT_RESOURCE = "manytomany.cfg.xml";
private static final Logger LOGGER = LoggerFactory.getLogger(HibernateUtil.class);
private static SessionFactory sessionFactory;
private static SessionFactory buildSessionFactory() {
private static SessionFactory buildSessionFactory(String resource) {
try {
// Create the SessionFactory from hibernate-annotation.cfg.xml
Configuration configuration = new Configuration();
@ -29,16 +32,16 @@ public class HibernateUtil {
configuration.addAnnotatedClass(Element.class);
configuration.addAnnotatedClass(Reservation.class);
configuration.addAnnotatedClass(Sale.class);
configuration.configure("manytomany.cfg.xml");
configuration.addAnnotatedClass(Question.class);
configuration.addPackage(Question.class.getPackageName());
configuration.configure(resource);
LOGGER.debug("Hibernate Annotation Configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties())
.build();
LOGGER.debug("Hibernate Annotation serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
return configuration.buildSessionFactory(serviceRegistry);
} catch (Throwable ex) {
LOGGER.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
@ -46,9 +49,10 @@ public class HibernateUtil {
}
public static SessionFactory getSessionFactory() {
if (sessionFactory == null) {
sessionFactory = buildSessionFactory();
}
return sessionFactory;
return buildSessionFactory(DEFAULT_RESOURCE);
}
public static SessionFactory getSessionFactory(String resource) {
return buildSessionFactory(resource);
}
}

View File

@ -0,0 +1,86 @@
package com.baeldung.hibernate.booleanconverters.model;
import java.util.UUID;
import org.hibernate.type.NumericBooleanConverter;
import org.hibernate.type.TrueFalseConverter;
import org.hibernate.type.YesNoConverter;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Question {
@Id
private UUID id;
private String content;
@Convert(converter = YesNoConverter.class)
private Boolean correctAnswer;
@Convert(converter = TrueFalseConverter.class)
private Boolean shouldBeAsked;
@Convert(converter = NumericBooleanConverter.class)
private Boolean isEasy;
private Boolean wasAskedBefore;
public Question() {
}
public Question(UUID id, String content, Boolean correctAnswer, Boolean shouldBeAsked, Boolean isEasy, Boolean wasAskedBefore) {
this.id = id;
this.content = content;
this.correctAnswer = correctAnswer;
this.shouldBeAsked = shouldBeAsked;
this.isEasy = isEasy;
this.wasAskedBefore = wasAskedBefore;
}
public UUID getId() {
return id;
}
public String getContent() {
return content;
}
public Boolean getCorrectAnswer() {
return correctAnswer;
}
public Boolean getShouldBeAsked() {
return shouldBeAsked;
}
public Boolean isEasy() {
return isEasy;
}
public Boolean getWasAskedBefore() {
return wasAskedBefore;
}
public void setId(UUID id) {
this.id = id;
}
public void setContent(String content) {
this.content = content;
}
public void setCorrectAnswer(Boolean correctAnswer) {
this.correctAnswer = correctAnswer;
}
public void setShouldBeAsked(Boolean shouldBeAsked) {
this.shouldBeAsked = shouldBeAsked;
}
public void setEasy(Boolean easy) {
isEasy = easy;
}
public void setWasAskedBefore(Boolean wasAskedBefore) {
this.wasAskedBefore = wasAskedBefore;
}
}

View File

@ -0,0 +1,5 @@
@ConverterRegistration(converter = YesNoConverter.class)
package com.baeldung.hibernate.booleanconverters.model;
import org.hibernate.annotations.ConverterRegistration;
import org.hibernate.type.YesNoConverter;

View File

@ -1,4 +1,4 @@
package com.baeldung.manytomany;
package com.baeldung.hibernate.manytomany;
import java.util.Properties;
@ -22,7 +22,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@PropertySource({ "classpath:persistence-h2.properties" })
@ComponentScan({ "com.baeldung.manytomany" })
@ComponentScan({ "com.baeldung.hibernate.manytomany" })
public class PersistenceConfig {
@Autowired
@ -32,7 +32,7 @@ public class PersistenceConfig {
public LocalSessionFactoryBean sessionFactory() {
final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(restDataSource());
sessionFactory.setPackagesToScan(new String[] { "com.baeldung.manytomany" });
sessionFactory.setPackagesToScan(new String[] { "com.baeldung.hibernate.manytomany" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;

View File

@ -0,0 +1,8 @@
package com.baeldung.hibernate.manytomany.dao;
import com.baeldung.hibernate.manytomany.dao.common.IOperations;
import com.baeldung.hibernate.manytomany.model.Employee;
public interface IEmployeeDao extends IOperations<Employee>{
}

View File

@ -0,0 +1,8 @@
package com.baeldung.hibernate.manytomany.dao;
import com.baeldung.hibernate.manytomany.dao.common.IOperations;
import com.baeldung.hibernate.manytomany.model.Project;
public interface IProjectDao extends IOperations<Project>{
}

View File

@ -1,4 +1,4 @@
package com.baeldung.manytomany.dao.common;
package com.baeldung.hibernate.manytomany.dao.common;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package com.baeldung.manytomany.dao.common;
package com.baeldung.hibernate.manytomany.dao.common;
import java.io.Serializable;
import java.util.List;

View File

@ -1,4 +1,4 @@
package com.baeldung.manytomany.dao.common;
package com.baeldung.hibernate.manytomany.dao.common;
import java.io.Serializable;
import java.util.List;

View File

@ -1,10 +1,10 @@
package com.baeldung.manytomany.dao.impl;
package com.baeldung.hibernate.manytomany.dao.impl;
import org.springframework.stereotype.Repository;
import com.baeldung.manytomany.dao.IEmployeeDao;
import com.baeldung.manytomany.dao.common.AbstractHibernateDao;
import com.baeldung.manytomany.model.Employee;
import com.baeldung.hibernate.manytomany.dao.IEmployeeDao;
import com.baeldung.hibernate.manytomany.dao.common.AbstractHibernateDao;
import com.baeldung.hibernate.manytomany.model.Employee;
@Repository
public class EmployeeDao extends AbstractHibernateDao<Employee> implements IEmployeeDao {

View File

@ -0,0 +1,17 @@
package com.baeldung.hibernate.manytomany.dao.impl;
import org.springframework.stereotype.Repository;
import com.baeldung.hibernate.manytomany.dao.IProjectDao;
import com.baeldung.hibernate.manytomany.dao.common.AbstractHibernateDao;
import com.baeldung.hibernate.manytomany.model.Project;
@Repository
public class ProjectDao extends AbstractHibernateDao<Project> implements IProjectDao {
public ProjectDao() {
super();
setClazz(Project.class);
}
}

View File

@ -1,4 +1,4 @@
package com.baeldung.manytomany.model;
package com.baeldung.hibernate.manytomany.model;
import java.io.Serializable;
import java.util.HashSet;

View File

@ -1,4 +1,4 @@
package com.baeldung.manytomany.model;
package com.baeldung.hibernate.manytomany.model;
import java.io.Serializable;
import java.util.HashSet;

View File

@ -1,4 +1,4 @@
package com.baeldung.uuids;
package com.baeldung.hibernate.uuids;
import java.util.UUID;
import jakarta.persistence.Entity;

View File

@ -1,4 +1,4 @@
package com.baeldung.uuids;
package com.baeldung.hibernate.uuids;
import java.util.UUID;
import jakarta.persistence.Id;

View File

@ -1,4 +1,4 @@
package com.baeldung.uuids;
package com.baeldung.hibernate.uuids;
import jakarta.persistence.Id;
import jakarta.persistence.Entity;

View File

@ -1,4 +1,4 @@
package com.baeldung.uuids;
package com.baeldung.hibernate.uuids;
import java.util.UUID;
import java.time.LocalDate;

View File

@ -1,8 +0,0 @@
package com.baeldung.manytomany.dao;
import com.baeldung.manytomany.dao.common.IOperations;
import com.baeldung.manytomany.model.Employee;
public interface IEmployeeDao extends IOperations<Employee>{
}

View File

@ -1,8 +0,0 @@
package com.baeldung.manytomany.dao;
import com.baeldung.manytomany.dao.common.IOperations;
import com.baeldung.manytomany.model.Project;
public interface IProjectDao extends IOperations<Project>{
}

View File

@ -1,18 +0,0 @@
package com.baeldung.manytomany.dao.impl;
import org.springframework.stereotype.Repository;
import com.baeldung.manytomany.dao.IProjectDao;
import com.baeldung.manytomany.dao.common.AbstractHibernateDao;
import com.baeldung.manytomany.model.Project;
@Repository
public class ProjectDao extends AbstractHibernateDao<Project> implements IProjectDao {
public ProjectDao() {
super();
setClazz(Project.class);
}
}

View File

@ -6,7 +6,7 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.baeldung.manytomany.PersistenceConfig;
import com.baeldung.hibernate.manytomany.PersistenceConfig;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { PersistenceConfig.class }, loader = AnnotationConfigContextLoader.class)

View File

@ -0,0 +1,158 @@
package com.baeldung.hibernate.booleanconverters;
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.UUID;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.baeldung.hibernate.HibernateUtil;
import com.baeldung.hibernate.booleanconverters.model.Question;
public class HibernateBooleanConverterIntegrationTest {
private static final String PROPERTY_FILE_NAME = "booleanconverters.cfg.xml";
private static SessionFactory sessionFactory;
private static Session session;
@BeforeAll
static void createSessionFactory() {
sessionFactory = HibernateUtil.getSessionFactory(PROPERTY_FILE_NAME);
}
@BeforeEach
void openSessionAndBeginTransaction() {
session = sessionFactory.openSession();
}
@AfterAll
static void closeSessionFactory() {
sessionFactory.close();
}
@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);
}
@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);
}
@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);
}
@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());
}
@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);
}
}

View File

@ -0,0 +1,48 @@
package com.baeldung.hibernate.booleanconverters;
import java.util.UUID;
import com.baeldung.hibernate.booleanconverters.model.Question;
public class QuestionBuilder {
private UUID id;
private String content;
private Boolean correctAnswer;
private Boolean shouldBeAsked;
private Boolean isEasy;
private Boolean wasAskedBefore;
public QuestionBuilder id(UUID id) {
this.id = id;
return this;
}
public QuestionBuilder content(String content) {
this.content = content;
return this;
}
public QuestionBuilder correctAnswer(Boolean correctAnswer) {
this.correctAnswer = correctAnswer;
return this;
}
public QuestionBuilder shouldBeAsked(Boolean shouldBeAsked) {
this.shouldBeAsked = shouldBeAsked;
return this;
}
public QuestionBuilder isEasy(Boolean isEasy) {
this.isEasy = isEasy;
return this;
}
public QuestionBuilder wasAskedBefore(Boolean wasAskedBefore) {
this.wasAskedBefore = wasAskedBefore;
return this;
}
public Question build() {
return new Question(id, content, correctAnswer, shouldBeAsked, isEasy, wasAskedBefore);
}
}

View File

@ -13,9 +13,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.baeldung.manytomany.PersistenceConfig;
import com.baeldung.manytomany.model.Employee;
import com.baeldung.manytomany.model.Project;
import com.baeldung.hibernate.manytomany.model.Employee;
import com.baeldung.hibernate.manytomany.model.Project;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { PersistenceConfig.class }, loader = AnnotationConfigContextLoader.class)

View File

@ -15,9 +15,9 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.baeldung.manytomany.model.Employee;
import com.baeldung.manytomany.model.Project;
import com.baeldung.HibernateUtil;
import com.baeldung.hibernate.manytomany.model.Employee;
import com.baeldung.hibernate.manytomany.model.Project;
import com.baeldung.hibernate.HibernateUtil;
/**
* Configured in: manytomany.cfg.xml

View File

@ -1,18 +1,12 @@
package com.baeldung.hibernate.uuids;
import com.baeldung.HibernateUtil;
import com.baeldung.hibernate.HibernateUtil;
import com.baeldung.uuids.Reservation;
import com.baeldung.uuids.Sale;
import com.baeldung.uuids.WebSiteUser;
import com.baeldung.uuids.Element;
import org.assertj.core.api.Assertions;
import java.io.IOException;
import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import java.util.UUID;

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.h2.Driver</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.url">jdbc:h2:mem:booleanconvertersdb;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM 'src/test/resources/booleanconverters/init_database.sql'</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">false</property>
<property name="hibernate.hbm2ddl.auto">none</property>
</session-factory>
</hibernate-configuration>

View File

@ -0,0 +1,9 @@
CREATE TABLE Question (
id UUID,
content VARCHAR,
correctAnswer CHAR,
shouldBeAsked CHAR,
isEasy TINYINT,
wasAskedBefore CHAR,
PRIMARY KEY (id)
)