Skip to content

Commit 393874e

Browse files
committed
Warn users about the use of built-in Quarkus' format mappers
1 parent 8535990 commit 393874e

File tree

15 files changed

+251
-22
lines changed

15 files changed

+251
-22
lines changed

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
88
import io.quarkus.hibernate.orm.runtime.config.DatabaseOrmCompatibilityVersion;
9+
import io.quarkus.hibernate.orm.runtime.customized.BuiltinFormatMapperBehaviour;
910
import io.quarkus.runtime.annotations.ConfigDocMapKey;
1011
import io.quarkus.runtime.annotations.ConfigDocSection;
1112
import io.quarkus.runtime.annotations.ConfigGroup;
@@ -53,6 +54,13 @@ public interface HibernateOrmConfig {
5354
@ConfigDocSection
5455
HibernateOrmConfigDatabase database();
5556

57+
/**
58+
* JSON/XML mapping related configuration.
59+
*/
60+
@Deprecated(since = "3.24", forRemoval = true)
61+
@ConfigDocSection
62+
HibernateOrmConfigMapping mapping();
63+
5664
/**
5765
* Configuration for persistence units.
5866
*/
@@ -210,4 +218,27 @@ interface HibernateOrmConfigDevUI {
210218
@WithDefault("false")
211219
boolean allowHql();
212220
}
221+
222+
@Deprecated(since = "3.24", forRemoval = true)
223+
@ConfigGroup
224+
interface HibernateOrmConfigMapping {
225+
226+
/**
227+
* Mapping format.
228+
*/
229+
HibernateOrmConfigMappingFormat format();
230+
231+
@Deprecated(since = "3.24", forRemoval = true)
232+
@ConfigGroup
233+
interface HibernateOrmConfigMappingFormat {
234+
/**
235+
* How the default JSON/XML format mappers are configured.
236+
*
237+
* @deprecated Only available to mitigate migration from the Quarkus preconfigured current mappers
238+
*/
239+
@Deprecated(since = "3.24", forRemoval = true)
240+
@WithDefault("warn")
241+
BuiltinFormatMapperBehaviour global();
242+
}
243+
}
213244
}

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,9 @@ public void configurationDescriptorBuilding(
324324
.filter(i -> i.isDefault())
325325
.findFirst();
326326
collectDialectConfigForPersistenceXml(puName, xmlDescriptor);
327-
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities);
328-
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities);
327+
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities,
328+
hibernateOrmConfig.mapping().format().global());
329+
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities, hibernateOrmConfig.mapping().format().global());
329330
jsonMapper.flatMap(FormatMapperKind::requiredBeanType)
330331
.ifPresent(type -> unremovableBeans.produce(UnremovableBeanBuildItem.beanClassNames(type)));
331332
xmlMapper.flatMap(FormatMapperKind::requiredBeanType)
@@ -341,7 +342,8 @@ public void configurationDescriptorBuilding(
341342
getMultiTenancyStrategy(
342343
Optional.ofNullable(persistenceXmlDescriptorBuildItem.getDescriptor()
343344
.getProperties().getProperty("hibernate.multiTenancy"))), //FIXME this property is meaningless in Hibernate ORM 6
344-
hibernateOrmConfig.database().ormCompatibilityVersion(), Collections.emptyMap()),
345+
hibernateOrmConfig.database().ormCompatibilityVersion(),
346+
hibernateOrmConfig.mapping().format().global(), Collections.emptyMap()),
345347
null,
346348
jpaModel.getXmlMappings(persistenceXmlDescriptorBuildItem.getDescriptor().getName()),
347349
true, isHibernateValidatorPresent(capabilities), jsonMapper, xmlMapper));
@@ -948,8 +950,8 @@ private static void producePersistenceUnitDescriptorFromConfig(
948950
configureSqlLoadScript(persistenceUnitName, persistenceUnitConfig, applicationArchivesBuildItem, launchMode,
949951
nativeImageResources, hotDeploymentWatchedFiles, descriptor);
950952

951-
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities);
952-
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities);
953+
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities, hibernateOrmConfig.mapping().format().global());
954+
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities, hibernateOrmConfig.mapping().format().global());
953955
jsonMapper.flatMap(FormatMapperKind::requiredBeanType)
954956
.ifPresent(type -> unremovableBeans.produce(UnremovableBeanBuildItem.beanClassNames(type)));
955957
xmlMapper.flatMap(FormatMapperKind::requiredBeanType)
@@ -963,6 +965,7 @@ private static void producePersistenceUnitDescriptorFromConfig(
963965
persistenceUnitConfig.dialect().dialect(),
964966
multiTenancyStrategy,
965967
hibernateOrmConfig.database().ormCompatibilityVersion(),
968+
hibernateOrmConfig.mapping().format().global(),
966969
persistenceUnitConfig.unsupportedProperties()),
967970
persistenceUnitConfig.multitenantSchemaDatasource().orElse(null),
968971
xmlMappings,

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/util/HibernateProcessorUtil.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import io.quarkus.hibernate.orm.deployment.spi.DatabaseKindDialectBuildItem;
4040
import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig;
4141
import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDescriptor;
42+
import io.quarkus.hibernate.orm.runtime.customized.BuiltinFormatMapperBehaviour;
4243
import io.quarkus.hibernate.orm.runtime.customized.FormatMapperKind;
4344
import io.quarkus.runtime.LaunchMode;
4445
import io.quarkus.runtime.configuration.ConfigurationException;
@@ -58,7 +59,10 @@ public static boolean hasEntities(JpaModelBuildItem jpaModel) {
5859
return !jpaModel.getEntityClassNames().isEmpty();
5960
}
6061

61-
public static Optional<FormatMapperKind> jsonMapperKind(Capabilities capabilities) {
62+
public static Optional<FormatMapperKind> jsonMapperKind(Capabilities capabilities, BuiltinFormatMapperBehaviour behaviour) {
63+
if (BuiltinFormatMapperBehaviour.IGNORE.equals(behaviour)) {
64+
return Optional.empty();
65+
}
6266
if (capabilities.isPresent(Capability.JACKSON)) {
6367
return Optional.of(FormatMapperKind.JACKSON);
6468
} else if (capabilities.isPresent(Capability.JSONB)) {
@@ -68,7 +72,10 @@ public static Optional<FormatMapperKind> jsonMapperKind(Capabilities capabilitie
6872
}
6973
}
7074

71-
public static Optional<FormatMapperKind> xmlMapperKind(Capabilities capabilities) {
75+
public static Optional<FormatMapperKind> xmlMapperKind(Capabilities capabilities, BuiltinFormatMapperBehaviour behaviour) {
76+
if (BuiltinFormatMapperBehaviour.IGNORE.equals(behaviour)) {
77+
return Optional.empty();
78+
}
7279
return capabilities.isPresent(Capability.JAXB)
7380
? Optional.of(FormatMapperKind.JAXB)
7481
: Optional.empty();

extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String
191191
throw new IllegalStateException(
192192
"Attempting to boot a deactivated Hibernate ORM persistence unit");
193193
}
194+
194195
RuntimeSettings runtimeSettings = buildRuntimeSettings(persistenceUnitName, recordedState, puConfig);
195196

196197
StandardServiceRegistry standardServiceRegistry = rewireMetadataAndExtractServiceRegistry(persistenceUnitName,
@@ -205,7 +206,8 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String
205206
standardServiceRegistry /* Mostly ignored! (yet needs to match) */,
206207
runtimeSettings,
207208
validatorFactory, cdiBeanManager, recordedState.getMultiTenancyStrategy(),
208-
true);
209+
true,
210+
recordedState.getBuildTimeSettings().getSource().getBuiltinFormatMapperBehaviour());
209211
}
210212

211213
log.debug("Found no matching persistence units");

extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public JPAConfig(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) {
4646

4747
void startAll() {
4848
List<CompletableFuture<?>> start = new ArrayList<>();
49-
//by using a dedicated thread for starting up the PR,
49+
//by using a dedicated thread for starting up the PU,
5050
//we work around https://github.com/quarkusio/quarkus/issues/17304 to some extent
5151
//as the main thread is now no longer polluted with ThreadLocals by default
5252
//this is not a complete fix, but will help as long as the test methods

extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootEntityManagerFactoryBuilder.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import io.quarkus.hibernate.orm.XmlFormat;
3636
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
3737
import io.quarkus.hibernate.orm.runtime.RuntimeSettings;
38+
import io.quarkus.hibernate.orm.runtime.customized.BuiltinFormatMapperBehaviour;
3839
import io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy;
3940
import io.quarkus.hibernate.orm.runtime.observers.QuarkusSessionFactoryObserverForDbVersionCheck;
4041
import io.quarkus.hibernate.orm.runtime.observers.SessionFactoryObserverForNamedQueryValidation;
@@ -50,6 +51,7 @@ public class FastBootEntityManagerFactoryBuilder implements EntityManagerFactory
5051
private final RuntimeSettings runtimeSettings;
5152
private final Object validatorFactory;
5253
private final Object cdiBeanManager;
54+
private final BuiltinFormatMapperBehaviour builtinFormatMapperBehaviour;
5355

5456
protected final MultiTenancyStrategy multiTenancyStrategy;
5557
protected final boolean shouldApplySchemaMigration;
@@ -58,7 +60,8 @@ public FastBootEntityManagerFactoryBuilder(
5860
QuarkusPersistenceUnitDescriptor puDescriptor,
5961
PrevalidatedQuarkusMetadata metadata,
6062
StandardServiceRegistry standardServiceRegistry, RuntimeSettings runtimeSettings, Object validatorFactory,
61-
Object cdiBeanManager, MultiTenancyStrategy multiTenancyStrategy, boolean shouldApplySchemaMigration) {
63+
Object cdiBeanManager, MultiTenancyStrategy multiTenancyStrategy, boolean shouldApplySchemaMigration,
64+
BuiltinFormatMapperBehaviour builtinFormatMapperBehaviour) {
6265
this.puDescriptor = puDescriptor;
6366
this.metadata = metadata;
6467
this.standardServiceRegistry = standardServiceRegistry;
@@ -67,6 +70,7 @@ public FastBootEntityManagerFactoryBuilder(
6770
this.cdiBeanManager = cdiBeanManager;
6871
this.multiTenancyStrategy = multiTenancyStrategy;
6972
this.shouldApplySchemaMigration = shouldApplySchemaMigration;
73+
this.builtinFormatMapperBehaviour = builtinFormatMapperBehaviour;
7074
}
7175

7276
@Override
@@ -211,11 +215,15 @@ protected void populate(String persistenceUnitName, SessionFactoryOptionsBuilder
211215
FormatMapper.class, persistenceUnitName, JsonFormat.Literal.INSTANCE);
212216
if (!jsonFormatMapper.isUnsatisfied()) {
213217
options.applyJsonFormatMapper(jsonFormatMapper.get());
218+
} else {
219+
builtinFormatMapperBehaviour.jsonApply(metadata(), persistenceUnitName);
214220
}
215221
InjectableInstance<FormatMapper> xmlFormatMapper = PersistenceUnitUtil.singleExtensionInstanceForPersistenceUnit(
216222
FormatMapper.class, persistenceUnitName, XmlFormat.Literal.INSTANCE);
217223
if (!xmlFormatMapper.isUnsatisfied()) {
218224
options.applyXmlFormatMapper(xmlFormatMapper.get());
225+
} else {
226+
builtinFormatMapperBehaviour.xmlApply(metadata(), persistenceUnitName);
219227
}
220228
}
221229

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.quarkus.hibernate.orm.runtime.customized;
2+
3+
import java.util.List;
4+
import java.util.Locale;
5+
import java.util.Set;
6+
7+
import org.hibernate.boot.spi.MetadataImplementor;
8+
import org.hibernate.mapping.Collection;
9+
import org.hibernate.mapping.Column;
10+
import org.hibernate.mapping.PersistentClass;
11+
import org.hibernate.mapping.Property;
12+
import org.hibernate.type.SqlTypes;
13+
import org.jboss.logging.Logger;
14+
15+
@Deprecated(since = "3.24", forRemoval = true)
16+
public enum BuiltinFormatMapperBehaviour {
17+
/**
18+
* The Quarkus preconfigured mappers are ignored and if there is no user provided one,
19+
* Hibernate ORM will create a mapper according to its own rules.
20+
*/
21+
IGNORE {
22+
@Override
23+
protected void action(String puName, String type) {
24+
}
25+
},
26+
/**
27+
* Currently the default one, uses a Quarkus preconfigured format mappers. If a format mapper operation is invoked a
28+
* warning is logged.
29+
*/
30+
WARN {
31+
@Override
32+
protected void action(String puName, String type) {
33+
LOGGER.warnf(MESSAGE_TEMPLATE, puName, type);
34+
}
35+
},
36+
/**
37+
* If there is no user provided format mapper, a Quarkus preconfigured one will fail at runtime.
38+
* Will become the default in the future versions of Quarkus.
39+
*/
40+
FAIL {
41+
@Override
42+
protected void action(String puName, String type) {
43+
throw new IllegalStateException(String.format(Locale.ROOT, MESSAGE_TEMPLATE, puName, type));
44+
}
45+
};
46+
47+
private static final Logger LOGGER = Logger.getLogger(BuiltinFormatMapperBehaviour.class);
48+
49+
private static final String MESSAGE_TEMPLATE = "Persistence unit [%1$s] uses Quarkus' main formatting facilities for %2$s columns in the database. "
50+
+ "As these facilities are primarily meant for REST calls, and they may have been customized for such use, "
51+
+ "this may lead to undesired behavior, up to and including data loss. "
52+
+ "Future version of Quarkus will use Hibernate ORM's own separate formatting instead. "
53+
+ "If the application does not customize the %2$s serialization/deserialization, set \"quarkus.hibernate-orm.mapping.format.global=ignore\". "
54+
+ "Otherwise, create a custom format mapper to address your database serialization/deserialization needs, "
55+
+ "assign it to be used by the [%1$s] persistence unit and confirm that %2$s data is correctly serialized/deserialized to/from the database."
56+
+ "See the migration guide for more details and how to proceed.";
57+
58+
public static boolean hasJsonProperties(MetadataImplementor metadata) {
59+
return hasXxxProperties(metadata, Set.of(SqlTypes.JSON, SqlTypes.JSON_ARRAY));
60+
}
61+
62+
public static boolean hasXmlProperties(MetadataImplementor metadata) {
63+
return hasXxxProperties(metadata, Set.of(SqlTypes.SQLXML, SqlTypes.XML_ARRAY));
64+
}
65+
66+
private static boolean hasXxxProperties(MetadataImplementor metadata, Set<Integer> propertyTypeNames) {
67+
for (PersistentClass persistentClass : metadata.getEntityBindings()) {
68+
for (Property property : persistentClass.getProperties()) {
69+
List<Column> columns = property.getColumns();
70+
if (columns.isEmpty()) {
71+
if (property.getValue() instanceof Collection c) {
72+
columns = c.getElement().getColumns();
73+
}
74+
}
75+
for (Column column : columns) {
76+
if (propertyTypeNames.contains(column.getSqlTypeCode(metadata))) {
77+
return true;
78+
}
79+
}
80+
}
81+
}
82+
return false;
83+
}
84+
85+
public void jsonApply(MetadataImplementor metadata, String puName) {
86+
if (hasJsonProperties(metadata)) {
87+
action(puName, "JSON");
88+
}
89+
}
90+
91+
public void xmlApply(MetadataImplementor metadata, String puName) {
92+
if (hasXmlProperties(metadata)) {
93+
action(puName, "XML");
94+
}
95+
}
96+
97+
protected abstract void action(String puName, String type);
98+
}

extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/customized/FormatMapperKind.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import io.quarkus.arc.Arc;
1515

16+
@Deprecated(since = "3.24", forRemoval = true)
1617
public enum FormatMapperKind {
1718
JACKSON {
1819
@Override

extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/recording/RecordedConfig.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Optional;
66

77
import io.quarkus.hibernate.orm.runtime.config.DatabaseOrmCompatibilityVersion;
8+
import io.quarkus.hibernate.orm.runtime.customized.BuiltinFormatMapperBehaviour;
89
import io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy;
910
import io.quarkus.runtime.annotations.RecordableConstructor;
1011

@@ -19,12 +20,14 @@ public class RecordedConfig {
1920
private final MultiTenancyStrategy multiTenancyStrategy;
2021
private final Map<String, String> quarkusConfigUnsupportedProperties;
2122
private final DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion;
23+
private final BuiltinFormatMapperBehaviour builtinFormatMapperBehaviour;
2224

2325
@RecordableConstructor
2426
public RecordedConfig(Optional<String> dataSource, Optional<String> dbKind,
2527
Optional<String> dbVersion, Optional<String> explicitDialect,
2628
MultiTenancyStrategy multiTenancyStrategy,
2729
DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion,
30+
BuiltinFormatMapperBehaviour builtinFormatMapperBehaviour,
2831
Map<String, String> quarkusConfigUnsupportedProperties) {
2932
Objects.requireNonNull(dataSource);
3033
Objects.requireNonNull(dbKind);
@@ -35,8 +38,9 @@ public RecordedConfig(Optional<String> dataSource, Optional<String> dbKind,
3538
this.dbVersion = dbVersion;
3639
this.explicitDialect = explicitDialect;
3740
this.multiTenancyStrategy = multiTenancyStrategy;
38-
this.quarkusConfigUnsupportedProperties = quarkusConfigUnsupportedProperties;
3941
this.databaseOrmCompatibilityVersion = databaseOrmCompatibilityVersion;
42+
this.builtinFormatMapperBehaviour = builtinFormatMapperBehaviour;
43+
this.quarkusConfigUnsupportedProperties = quarkusConfigUnsupportedProperties;
4044
}
4145

4246
public Optional<String> getDataSource() {
@@ -59,11 +63,15 @@ public MultiTenancyStrategy getMultiTenancyStrategy() {
5963
return multiTenancyStrategy;
6064
}
6165

62-
public Map<String, String> getQuarkusConfigUnsupportedProperties() {
63-
return quarkusConfigUnsupportedProperties;
64-
}
65-
6666
public DatabaseOrmCompatibilityVersion getDatabaseOrmCompatibilityVersion() {
6767
return databaseOrmCompatibilityVersion;
6868
}
69+
70+
public BuiltinFormatMapperBehaviour getBuiltinFormatMapperBehaviour() {
71+
return builtinFormatMapperBehaviour;
72+
}
73+
74+
public Map<String, String> getQuarkusConfigUnsupportedProperties() {
75+
return quarkusConfigUnsupportedProperties;
76+
}
6977
}

extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ public void buildReactivePersistenceUnit(
183183
launchMode.getLaunchMode(),
184184
systemProperties, nativeImageResources, hotDeploymentWatchedFiles, dbKindDialectBuildItems);
185185

186-
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities);
187-
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities);
186+
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities, hibernateOrmConfig.mapping().format().global());
187+
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities, hibernateOrmConfig.mapping().format().global());
188188
jsonMapper.flatMap(FormatMapperKind::requiredBeanType)
189189
.ifPresent(type -> unremovableBeans.produce(UnremovableBeanBuildItem.beanClassNames(type)));
190190
xmlMapper.flatMap(FormatMapperKind::requiredBeanType)
@@ -202,6 +202,7 @@ public void buildReactivePersistenceUnit(
202202
persistenceUnitConfig.dialect().dialect(),
203203
io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy.NONE,
204204
hibernateOrmConfig.database().ormCompatibilityVersion(),
205+
hibernateOrmConfig.mapping().format().global(),
205206
persistenceUnitConfig.unsupportedProperties()),
206207
null,
207208
jpaModel.getXmlMappings(reactivePU.getName()),

0 commit comments

Comments
 (0)