Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,51 @@ interface HibernateOrmConfigPersistenceUnitMapping {
*/
Id id();

Duration duration();

/**
* The preferred JDBC type to use for storing {@link java.time.Instant} values.
* <p>
* Can be overridden locally using `@JdbcType`, `@JdbcTypeCode`, and similar annotations.
* <p>
* Can also specify the name of the SqlTypes constant field,
* for example, `quarkus.hibernate-orm.mapping.type.preferred_instant_jdbc_type=TIMESTAMP`
* or `quarkus.hibernate-orm.mapping.type.preferred_instant_jdbc_type=INSTANT`.
*
* @asciidoclet
*/
@WithName("instant.preferred-jdbc-type")
@ConfigDocDefault("TIMESTAMP")
Optional<@WithConverter(TrimmedStringConverter.class) String> instantPreferredJdbcType();

/**
* The preferred JDBC type to use for storing boolean values.
* <p>
* Can be overridden locally using `@JdbcType`, `@JdbcTypeCode`, and similar annotations.
* <p>
* Can also specify the name of the SqlTypes constant field,
* for example, `quarkus.hibernate-orm.mapping.type.boolean_jdbc_type=BIT`.
*
* @asciidoclet
*/
@WithName("boolean.preferred-jdbc-type")
@ConfigDocDefault("BOOLEAN")
Optional<@WithConverter(TrimmedStringConverter.class) String> booleanPreferredJdbcType();

/**
* The preferred JDBC type to use for storing {@link java.util.UUID} values.
* <p>
* Can be overridden locally using `@JdbcType`, `@JdbcTypeCode`, and similar annotations.
* <p>
* Can also specify the name of the SqlTypes constant field,
* for example, `quarkus.hibernate-orm.mapping.type.uuid_jdbc_type=CHAR`.
*
* @asciidoclet
*/
@WithName("uuid.preferred-jdbc-type")
@ConfigDocDefault("UUID")
Optional<@WithConverter(TrimmedStringConverter.class) String> UUIDPreferredJdbcType();

@ConfigGroup
interface Timezone {
/**
Expand Down Expand Up @@ -436,9 +481,31 @@ interface Optimizer {
}
}

@ConfigGroup
interface Duration {

/**
* The preferred JDBC type to use for storing {@link java.time.Duration} values.
* <p>
* Can be overridden locally using `@JdbcType`, `@JdbcTypeCode`, and similar annotations.
* <p>
* Can also specify the name of the SqlTypes constant field,
* for example, `quarkus.hibernate-orm.mapping.type.preferred_jdbc_type=INTERVAL_SECOND`.
*
* @asciidoclet
*/
@WithName("preferred-jdbc-type")
@ConfigDocDefault("INTERVAL_SECOND")
Optional<@WithConverter(TrimmedStringConverter.class) String> durationPreferredJdbcType();
}

default boolean isAnyPropertySet() {
return timezone().timeZoneDefaultStorage().isPresent()
|| id().optimizer().idOptimizerDefault().isPresent();
return timezone().timeZoneDefaultStorage().isPresent() ||
id().optimizer().idOptimizerDefault().isPresent() ||
duration().durationPreferredJdbcType().isPresent() ||
instantPreferredJdbcType().isPresent() ||
booleanPreferredJdbcType().isPresent() ||
UUIDPreferredJdbcType().isPresent();
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,28 @@ public static void configureProperties(QuarkusPersistenceUnitDescriptor desc, Hi
config.mapping().id().optimizer().idOptimizerDefault()
.orElse(HibernateOrmConfigPersistenceUnit.IdOptimizerType.POOLED_LO).configName);

// Duration
config.mapping().duration().durationPreferredJdbcType().ifPresent(duration -> desc.getProperties().setProperty(
AvailableSettings.PREFERRED_DURATION_JDBC_TYPE,
duration));

// Instant
config.mapping().instantPreferredJdbcType().ifPresent(instant -> desc.getProperties().setProperty(
AvailableSettings.PREFERRED_INSTANT_JDBC_TYPE,
instant));

// Boolean
config.mapping().booleanPreferredJdbcType().ifPresent(
bool -> desc.getProperties().setProperty(
AvailableSettings.PREFERRED_BOOLEAN_JDBC_TYPE,
bool));

// UUID
config.mapping().UUIDPreferredJdbcType().ifPresent(
uuid -> desc.getProperties().setProperty(
AvailableSettings.PREFERRED_UUID_JDBC_TYPE,
uuid));

//charset
desc.getProperties()
.setProperty(AvailableSettings.HBM2DDL_CHARSET_NAME, config.database().charset().name());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ public class ConfigPropertiesTest {
// Overrides to test that Quarkus configuration properties are taken into account
.overrideConfigKey("quarkus.hibernate-orm.\"overrides\".flush.mode", "always")
.overrideConfigKey("quarkus.hibernate-orm.\"overrides\".schema-management.extra-physical-table-types",
"MATERIALIZED VIEW,FOREIGN TABLE");
"MATERIALIZED VIEW,FOREIGN TABLE")
.overrideConfigKey("quarkus.hibernate-orm.\"overrides\".mapping.duration.preferred-jdbc-type", "INTERVAL_SECOND")
.overrideConfigKey("quarkus.hibernate-orm.\"overrides\".mapping.instant.preferred-jdbc-type", "INSTANT")
.overrideConfigKey("quarkus.hibernate-orm.\"overrides\".mapping.boolean.preferred-jdbc-type", "BIT")
.overrideConfigKey("quarkus.hibernate-orm.\"overrides\".mapping.uuid.preferred-jdbc-type", "CHAR");

@Inject
Session sessionForDefaultPU;
Expand Down Expand Up @@ -67,4 +71,37 @@ public void extraPhysicalTableTypes() {
assertThat(tableTypes).containsExactly("MATERIALIZED VIEW", "FOREIGN TABLE");
}

@Test
@Transactional
void shouldMapHibernateOrmConfigPersistenceUnitMappingDurationProperties() {
// given
var preferredJdbcType = sessionForOverridesPU.getSessionFactory()
.getProperties()
.get(AvailableSettings.PREFERRED_DURATION_JDBC_TYPE);

// when - then
assertThat(preferredJdbcType).isEqualTo("INTERVAL_SECOND");
}

@Test
@Transactional
void shouldMapHibernateOrmConfigPersistenceUnitMappingPreferredTypesProperties() {
// given
var instantPreferredJdbcType = sessionForOverridesPU.getSessionFactory()
.getProperties()
.get(AvailableSettings.PREFERRED_INSTANT_JDBC_TYPE);

var booleanPreferredJdbcType = sessionForOverridesPU.getSessionFactory()
.getProperties()
.get(AvailableSettings.PREFERRED_BOOLEAN_JDBC_TYPE);

var UUIDPreferredJdbcType = sessionForOverridesPU.getSessionFactory()
.getProperties()
.get(AvailableSettings.PREFERRED_UUID_JDBC_TYPE);

// when - then
assertThat(instantPreferredJdbcType).isEqualTo("INSTANT");
assertThat(booleanPreferredJdbcType).isEqualTo("BIT");
assertThat(UUIDPreferredJdbcType).isEqualTo("CHAR");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:mem:test

#quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.schema-management.strategy=drop-and-create
quarkus.hibernate-orm.schema-management.strategy=drop-and-create
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.quarkus.it.jpa.preferredhibernatetypesoverride;

import java.time.Duration;
import java.time.Instant;
import java.util.UUID;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity(name = EntityWithOverridablePreferredTypes.NAME)
public class EntityWithOverridablePreferredTypes {
public static final String NAME = "ent_with_preferred_types";

@Id
@GeneratedValue
public UUID id;

public Instant createdAt;

public boolean isPersisted;

public Duration overridenDuration;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package io.quarkus.it.jpa.preferredhibernatetypesoverride;

import static org.assertj.core.api.Assertions.assertThat;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.UUID;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import org.hibernate.Session;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.StandardBasicTypes;

import io.quarkus.hibernate.orm.PersistenceUnit;

@Path("/overridden-preferred-types")
@ApplicationScoped
public class OverriddenPreferredTypesResource {

@Inject
@PersistenceUnit("overridden-types")
Session session;

@GET
@Path("/test-successful-persistence")
@Produces(MediaType.TEXT_PLAIN)
@Transactional
public String testSuccessfulPersistence() {
var entity = persistEntity();

assertThat(findUsingNativeQuery()).contains(entity.id);

return "OK";
}

@GET
@Path("/test-successful-override")
@Produces(MediaType.TEXT_PLAIN)
@Transactional
public String testSuccessfulPreferredTypesOverride() {
persistEntity();

var metamodel = session.getFactory()
.unwrap(SessionFactoryImplementor.class)
.getMappingMetamodel()
.findEntityDescriptor(EntityWithOverridablePreferredTypes.class);

assertThat(metamodel.getIdentifierMapping().getSingleJdbcMapping().getJdbcType()
.getDefaultSqlTypeCode())
.isEqualTo(SqlTypes.CHAR);
assertThat(metamodel.getAttributeMapping(metamodel.getPropertyIndex("createdAt")).getSingleJdbcMapping().getJdbcType()
.getDefaultSqlTypeCode())
.isEqualTo(SqlTypes.INSTANT);

assertThat(metamodel.getAttributeMapping(metamodel.getPropertyIndex("overridenDuration")).getSingleJdbcMapping()
.getJdbcType()
.getDefaultSqlTypeCode())
.isEqualTo(SqlTypes.INTERVAL_SECOND);

// Cannot detect BIT from the metamodel because it's handled as Boolean at runtime
// See https://github.com/hibernate/hibernate-orm/blob/018b8eeda3627e114ec25bd48407ccb9c47564ce/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanJdbcType.java#L61-L66
assertThat(metamodel.getAttributeMapping(metamodel.getPropertyIndex("isPersisted")).getSingleJdbcMapping().getJdbcType()
.getDefaultSqlTypeCode())
.isEqualTo(SqlTypes.BOOLEAN);

return "OK";
}

private EntityWithOverridablePreferredTypes persistEntity() {
var entity = new EntityWithOverridablePreferredTypes();
entity.isPersisted = true;
entity.createdAt = Instant.now();
entity.overridenDuration = Duration.ofDays(1);

session.persist(entity);
session.flush();
session.clear();

return entity;
}

private List<UUID> findUsingNativeQuery() {
return session.createNativeQuery(
"""
SELECT id FROM %s WHERE isPersisted = :isPersisted
""".formatted(EntityWithOverridablePreferredTypes.NAME))
.addScalar("id", StandardBasicTypes.UUID_CHAR)
.setParameter("isPersisted", true)
.getResultList();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:test
quarkus.hibernate-orm.packages=io.quarkus.it.jpa

quarkus.hibernate-orm.schema-management.strategy=drop-and-create

quarkus.hibernate-orm.metadata-builder-contributor=io.quarkus.it.jpa.defaultcatalogandschema.Schema1MetadataBuilderContributor

quarkus.hibernate-orm.multitenant=DISCRIMINATOR

quarkus.datasource."overridden-types".db-kind=h2
quarkus.hibernate-orm."overridden-types".datasource=overridden-types
quarkus.hibernate-orm."overridden-types".packages=io.quarkus.it.jpa.preferredhibernatetypesoverride
quarkus.hibernate-orm."overridden-types".mapping.duration.preferred-jdbc-type=INTERVAL_SECOND
quarkus.hibernate-orm."overridden-types".mapping.instant.preferred-jdbc-type=INSTANT
quarkus.hibernate-orm."overridden-types".mapping.boolean.preferred-jdbc-type=BIT
quarkus.hibernate-orm."overridden-types".mapping.uuid.preferred-jdbc-type=CHAR

dummy.transaction.timeout=30
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class JPAIntegratorTest {
public void testInjection() {
when().get("/jpa-test/integrator").then()
.statusCode(200)
.body(is("1"));
.body(is("2")); // Once per persistence unit
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.it.jpa.preferredhibernatetypesoverride;

import io.quarkus.test.junit.QuarkusIntegrationTest;

@QuarkusIntegrationTest
public class OverriddenPreferredTypesInGraalITCase extends OverriddenPreferredTypesTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.quarkus.it.jpa.preferredhibernatetypesoverride;

import static io.restassured.RestAssured.given;
import static org.hamcrest.core.Is.is;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
class OverriddenPreferredTypesTest {

@Test
void shouldSaveEntityWithOverriddenTypes() {
given().when().get("/jpa-test/overridden-preferred-types/test-successful-persistence").then()
.body(is("OK"))
.statusCode(200);
}

@Test
void shouldOverrideTypes() {
given().when().get("/jpa-test/overridden-preferred-types/test-successful-override").then()
.body(is("OK"))
.statusCode(200);
}
}
Loading