Skip to content

Commit f27237e

Browse files
committed
Warn users about the use of built-in Quarkus' format mappers
1 parent 01b7e87 commit f27237e

File tree

13 files changed

+238
-20
lines changed

13 files changed

+238
-20
lines changed

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

Lines changed: 25 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,10 @@ public interface HibernateOrmConfig {
5354
@ConfigDocSection
5455
HibernateOrmConfigDatabase database();
5556

57+
@Deprecated(since = "3.24", forRemoval = true)
58+
@ConfigDocSection
59+
HibernateOrmConfigXmlJsonMapping mapping();
60+
5661
/**
5762
* Configuration for persistence units.
5863
*/
@@ -210,4 +215,24 @@ interface HibernateOrmConfigDevUI {
210215
@WithDefault("false")
211216
boolean allowHql();
212217
}
218+
219+
@Deprecated(since = "3.24", forRemoval = true)
220+
@ConfigGroup
221+
interface HibernateOrmConfigXmlJsonMapping {
222+
223+
HibernateOrmConfigXmlJsonMappingFormat format();
224+
225+
@Deprecated(since = "3.24", forRemoval = true)
226+
@ConfigGroup
227+
interface HibernateOrmConfigXmlJsonMappingFormat {
228+
/**
229+
* How the default JSON/XML format mappers are configured.
230+
*
231+
* @deprecated Only available to mitigate migration from the Quarkus preconfigured current mappers
232+
*/
233+
@Deprecated(since = "3.24", forRemoval = true)
234+
@WithDefault("warn")
235+
BuiltinFormatMapperBehaviour global();
236+
}
237+
}
213238
}

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

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
import io.quarkus.hibernate.orm.runtime.boot.xml.QNameSubstitution;
128128
import io.quarkus.hibernate.orm.runtime.boot.xml.RecordableXmlMapping;
129129
import io.quarkus.hibernate.orm.runtime.config.DialectVersions;
130+
import io.quarkus.hibernate.orm.runtime.customized.BuiltinFormatMapperBehaviour;
130131
import io.quarkus.hibernate.orm.runtime.customized.FormatMapperKind;
131132
import io.quarkus.hibernate.orm.runtime.dev.HibernateOrmDevIntegrator;
132133
import io.quarkus.hibernate.orm.runtime.graal.RegisterServicesForReflectionFeature;
@@ -329,8 +330,9 @@ public void configurationDescriptorBuilding(
329330
.filter(i -> i.isDefault())
330331
.findFirst();
331332
collectDialectConfigForPersistenceXml(puName, xmlDescriptor);
332-
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities);
333-
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities);
333+
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities,
334+
hibernateOrmConfig.mapping().format().global());
335+
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities, hibernateOrmConfig.mapping().format().global());
334336
jsonMapper.flatMap(FormatMapperKind::requiredBeanType)
335337
.ifPresent(type -> unremovableBeans.produce(UnremovableBeanBuildItem.beanClassNames(type)));
336338
xmlMapper.flatMap(FormatMapperKind::requiredBeanType)
@@ -346,7 +348,8 @@ public void configurationDescriptorBuilding(
346348
getMultiTenancyStrategy(
347349
Optional.ofNullable(persistenceXmlDescriptorBuildItem.getDescriptor()
348350
.getProperties().getProperty("hibernate.multiTenancy"))), //FIXME this property is meaningless in Hibernate ORM 6
349-
hibernateOrmConfig.database().ormCompatibilityVersion(), Collections.emptyMap()),
351+
hibernateOrmConfig.database().ormCompatibilityVersion(),
352+
hibernateOrmConfig.mapping().format().global(), Collections.emptyMap()),
350353
null,
351354
jpaModel.getXmlMappings(persistenceXmlDescriptorBuildItem.getDescriptor().getName()),
352355
true, isHibernateValidatorPresent(capabilities), jsonMapper, xmlMapper));
@@ -1134,8 +1137,8 @@ private static void producePersistenceUnitDescriptorFromConfig(
11341137
descriptor.getProperties().setProperty(AvailableSettings.IGNORE_EXPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS,
11351138
String.valueOf(persistenceUnitConfig.discriminator().ignoreExplicitForJoined()));
11361139

1137-
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities);
1138-
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities);
1140+
Optional<FormatMapperKind> jsonMapper = jsonMapperKind(capabilities, hibernateOrmConfig.mapping().format().global());
1141+
Optional<FormatMapperKind> xmlMapper = xmlMapperKind(capabilities, hibernateOrmConfig.mapping().format().global());
11391142
jsonMapper.flatMap(FormatMapperKind::requiredBeanType)
11401143
.ifPresent(type -> unremovableBeans.produce(UnremovableBeanBuildItem.beanClassNames(type)));
11411144
xmlMapper.flatMap(FormatMapperKind::requiredBeanType)
@@ -1149,6 +1152,7 @@ private static void producePersistenceUnitDescriptorFromConfig(
11491152
persistenceUnitConfig.dialect().dialect(),
11501153
multiTenancyStrategy,
11511154
hibernateOrmConfig.database().ormCompatibilityVersion(),
1155+
hibernateOrmConfig.mapping().format().global(),
11521156
persistenceUnitConfig.unsupportedProperties()),
11531157
persistenceUnitConfig.multitenantSchemaDatasource().orElse(null),
11541158
xmlMappings,
@@ -1702,7 +1706,11 @@ private static boolean isMySQLOrMariaDB(Optional<String> dbKind, Optional<String
17021706
return false;
17031707
}
17041708

1705-
private static Optional<FormatMapperKind> jsonMapperKind(Capabilities capabilities) {
1709+
private static Optional<FormatMapperKind> jsonMapperKind(Capabilities capabilities,
1710+
BuiltinFormatMapperBehaviour behaviour) {
1711+
if (BuiltinFormatMapperBehaviour.IGNORE.equals(behaviour)) {
1712+
return Optional.empty();
1713+
}
17061714
if (capabilities.isPresent(Capability.JACKSON)) {
17071715
return Optional.of(FormatMapperKind.JACKSON);
17081716
}
@@ -1712,7 +1720,11 @@ private static Optional<FormatMapperKind> jsonMapperKind(Capabilities capabiliti
17121720
return Optional.empty();
17131721
}
17141722

1715-
private static Optional<FormatMapperKind> xmlMapperKind(Capabilities capabilities) {
1723+
private static Optional<FormatMapperKind> xmlMapperKind(Capabilities capabilities,
1724+
BuiltinFormatMapperBehaviour behaviour) {
1725+
if (BuiltinFormatMapperBehaviour.IGNORE.equals(behaviour)) {
1726+
return Optional.empty();
1727+
}
17161728
if (capabilities.isPresent(Capability.JAXB)) {
17171729
return Optional.of(FormatMapperKind.JAXB);
17181730
}

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
@@ -182,6 +182,7 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String
182182
throw new IllegalStateException(
183183
"Attempting to boot a deactivated Hibernate ORM persistence unit");
184184
}
185+
185186
RuntimeSettings runtimeSettings = buildRuntimeSettings(persistenceUnitName, recordedState, puConfig);
186187

187188
StandardServiceRegistry standardServiceRegistry = rewireMetadataAndExtractServiceRegistry(persistenceUnitName,
@@ -196,7 +197,8 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String
196197
standardServiceRegistry /* Mostly ignored! (yet needs to match) */,
197198
runtimeSettings,
198199
validatorFactory, cdiBeanManager, recordedState.getMultiTenancyStrategy(),
199-
true);
200+
true,
201+
recordedState.getBuildTimeSettings().getSource().getBuiltinFormatMapperBehaviour());
200202
}
201203

202204
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
@@ -217,11 +221,15 @@ protected void populate(String persistenceUnitName, SessionFactoryOptionsBuilder
217221
FormatMapper.class, persistenceUnitName, JsonFormat.Literal.INSTANCE);
218222
if (!jsonFormatMapper.isUnsatisfied()) {
219223
options.applyJsonFormatMapper(jsonFormatMapper.get());
224+
} else {
225+
builtinFormatMapperBehaviour.jsonApply(metadata(), persistenceUnitName);
220226
}
221227
InjectableInstance<FormatMapper> xmlFormatMapper = PersistenceUnitUtil.singleExtensionInstanceForPersistenceUnit(
222228
FormatMapper.class, persistenceUnitName, XmlFormat.Literal.INSTANCE);
223229
if (!xmlFormatMapper.isUnsatisfied()) {
224230
options.applyXmlFormatMapper(xmlFormatMapper.get());
231+
} else {
232+
builtinFormatMapperBehaviour.xmlApply(metadata(), persistenceUnitName);
225233
}
226234
}
227235

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package io.quarkus.hibernate.orm.runtime.customized;
2+
3+
import java.util.List;
4+
import java.util.Set;
5+
6+
import org.hibernate.boot.spi.MetadataImplementor;
7+
import org.hibernate.mapping.Collection;
8+
import org.hibernate.mapping.Column;
9+
import org.hibernate.mapping.PersistentClass;
10+
import org.hibernate.mapping.Property;
11+
import org.hibernate.type.SqlTypes;
12+
import org.jboss.logging.Logger;
13+
14+
@Deprecated(since = "3.24", forRemoval = true)
15+
public enum BuiltinFormatMapperBehaviour {
16+
/**
17+
* The Quarkus preconfigured mappers are ignored and if there is no user provided one,
18+
* Hibernate ORM will create a mapper according to its own rules.
19+
*/
20+
IGNORE {
21+
@Override
22+
protected void action(String puName, String type) {
23+
}
24+
},
25+
/**
26+
* Currently the default one, uses a Quarkus preconfigured format mappers. If a format mapper operation is invoked a
27+
* warning is logged.
28+
*/
29+
WARN {
30+
@Override
31+
protected void action(String puName, String type) {
32+
LOGGER.warnf(
33+
"Persistence unit [%1$s] uses the Quarkus' builtin format mappers for %2$s properties. This may lead to undesired behavior. "
34+
+ "These mappers will be removed in the future version of Quarkus. See the migration guide for more details and how to proceed.",
35+
puName, type);
36+
}
37+
},
38+
/**
39+
* If there is no user provided format mapper, a Quarkus preconfigured one will fail at runtime.
40+
* Will become the default in the future versions of Quarkus.
41+
*/
42+
FAIL {
43+
@Override
44+
protected void action(String puName, String type) {
45+
throw new IllegalStateException(
46+
"Using the Quarkus' builtin format mappers for JSON/XML properties is not allowed.");
47+
}
48+
};
49+
50+
private static final Logger LOGGER = Logger.getLogger(BuiltinFormatMapperBehaviour.class);
51+
52+
public static boolean hasJsonProperties(MetadataImplementor metadata) {
53+
return hasXxxProperties(metadata, Set.of(SqlTypes.JSON, SqlTypes.JSON_ARRAY));
54+
}
55+
56+
public static boolean hasXmlProperties(MetadataImplementor metadata) {
57+
return hasXxxProperties(metadata, Set.of(SqlTypes.SQLXML, SqlTypes.XML_ARRAY));
58+
}
59+
60+
private static boolean hasXxxProperties(MetadataImplementor metadata, Set<Integer> propertyTypeNames) {
61+
for (PersistentClass persistentClass : metadata.getEntityBindings()) {
62+
for (Property property : persistentClass.getProperties()) {
63+
List<Column> columns = property.getColumns();
64+
if (columns.isEmpty()) {
65+
if (property.getValue() instanceof Collection c) {
66+
columns = c.getElement().getColumns();
67+
}
68+
}
69+
for (Column column : columns) {
70+
if (propertyTypeNames.contains(column.getSqlTypeCode(metadata))) {
71+
return true;
72+
}
73+
}
74+
}
75+
}
76+
return false;
77+
}
78+
79+
public void jsonApply(MetadataImplementor metadata, String puName) {
80+
if (hasJsonProperties(metadata)) {
81+
action(puName, "JSON");
82+
}
83+
}
84+
85+
public void xmlApply(MetadataImplementor metadata, String puName) {
86+
if (hasXmlProperties(metadata)) {
87+
action(puName, "XML");
88+
}
89+
}
90+
91+
protected abstract void action(String puName, String type);
92+
}

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ public void buildReactivePersistenceUnit(
214214
persistenceUnitConfig.dialect().dialect(),
215215
io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy.NONE,
216216
hibernateOrmConfig.database().ormCompatibilityVersion(),
217+
hibernateOrmConfig.mapping().format().global(),
217218
persistenceUnitConfig.unsupportedProperties()),
218219
null,
219220
jpaModel.getXmlMappings(reactivePU.getName()),

extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String
221221
standardServiceRegistry /* Mostly ignored! (yet needs to match) */,
222222
runtimeSettings,
223223
validatorFactory, cdiBeanManager, recordedState.getMultiTenancyStrategy(),
224-
PersistenceUnitsHolder.getPersistenceUnitDescriptors().size() == 1);
224+
PersistenceUnitsHolder.getPersistenceUnitDescriptors().size() == 1,
225+
recordedState.getBuildTimeSettings().getSource().getBuiltinFormatMapperBehaviour());
225226
}
226227

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

extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/boot/FastBootReactiveEntityManagerFactoryBuilder.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.quarkus.hibernate.orm.runtime.RuntimeSettings;
1212
import io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder;
1313
import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDescriptor;
14+
import io.quarkus.hibernate.orm.runtime.customized.BuiltinFormatMapperBehaviour;
1415
import io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy;
1516
import io.quarkus.hibernate.orm.runtime.recording.PrevalidatedQuarkusMetadata;
1617

@@ -20,9 +21,10 @@ public FastBootReactiveEntityManagerFactoryBuilder(QuarkusPersistenceUnitDescrip
2021
PrevalidatedQuarkusMetadata metadata,
2122
StandardServiceRegistry standardServiceRegistry, RuntimeSettings runtimeSettings, Object validatorFactory,
2223
Object cdiBeanManager, MultiTenancyStrategy strategy,
23-
boolean shouldApplySchemaMigration) {
24+
boolean shouldApplySchemaMigration,
25+
BuiltinFormatMapperBehaviour builtinFormatMapperBehaviour) {
2426
super(puDescriptor, metadata, standardServiceRegistry, runtimeSettings, validatorFactory,
25-
cdiBeanManager, strategy, shouldApplySchemaMigration);
27+
cdiBeanManager, strategy, shouldApplySchemaMigration, builtinFormatMapperBehaviour);
2628
}
2729

2830
@Override

0 commit comments

Comments
 (0)