HHH-15200 Add support for the SQLXML type
This commit is contained in:
parent
d0b7d7f4b5
commit
c18e611ed6
|
@ -55,6 +55,11 @@ dependencies {
|
|||
testImplementation testLibs.mockito
|
||||
testImplementation testLibs.mockitoInline
|
||||
|
||||
testImplementation jakartaLibs.jaxbApi
|
||||
testImplementation jakartaLibs.jaxb
|
||||
testImplementation jakartaLibs.jsonb
|
||||
testImplementation libs.jacksonXml
|
||||
|
||||
testRuntimeOnly testLibs.wildFlyTxnClient
|
||||
testRuntimeOnly(libs.ehcache3) {
|
||||
capabilities {
|
||||
|
|
|
@ -430,8 +430,22 @@ Can reference a
|
|||
`FormatMapper` instance,
|
||||
`FormatMapper` implementation `Class` reference,
|
||||
`FormatMapper` implementation class name (fully-qualified class name) or
|
||||
one of the following short hand constants `jackson` or `jsonb`.
|
||||
By default the first of the possible providers that is available in the runtime is used, according to the listing order.
|
||||
one of the following shorthand constants `jackson` or `jsonb`.
|
||||
By default, the first of the possible providers that is available in the runtime is used, according to the listing order.
|
||||
+
|
||||
Note that the default serialization format of collections can differ depending on the serialization library.
|
||||
|
||||
`*hibernate.type.xml_format_mapper*` (e.g. A fully-qualified class name, an instance, or a `Class` object reference)::
|
||||
Names a https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/type/FormatMapper.html[`FormatMapper`] implementation to be applied to the `SessionFactory` for XML serialization and deserialization.
|
||||
+
|
||||
Can reference a
|
||||
`FormatMapper` instance,
|
||||
`FormatMapper` implementation `Class` reference,
|
||||
`FormatMapper` implementation class name (fully-qualified class name) or
|
||||
one of the following shorthand constants `jackson-xml` or `jaxb`.
|
||||
By default, the first of the possible providers that is available in the runtime is used, according to the listing order.
|
||||
+
|
||||
Note that the default serialization format of collections can differ depending on the serialization library.
|
||||
|
||||
[[configurations-bytecode-enhancement]]
|
||||
=== Bytecode Enhancement Properties
|
||||
|
|
|
@ -1357,6 +1357,23 @@ include::{sourcedir}/basic/JsonMappingTests.java[tags=basic-json-example]
|
|||
----
|
||||
====
|
||||
|
||||
[[basic-mapping-xml]]
|
||||
==== XML mapping
|
||||
|
||||
Hibernate will only use the `XML` type if explicitly configured through `@JdbcTypeCode( SqlTypes.SQLXML )`.
|
||||
The XML library used for serialization/deserialization is detected automatically,
|
||||
but can be overridden by setting `hibernate.type.xml_format_mapper`
|
||||
as can be read in the <<appendices/Configurations.adoc#misc-options,Configurations>> section.
|
||||
|
||||
[[basic-xml-example]]
|
||||
.Mapping XML
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/basic/XmlMappingTests.java[tags=basic-xml-example]
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[basic-mapping-composition]]
|
||||
==== Compositional basic mapping
|
||||
|
|
|
@ -6,21 +6,23 @@
|
|||
*/
|
||||
package org.hibernate.userguide.mapping.basic;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
|
@ -36,40 +38,68 @@ import static org.hamcrest.Matchers.is;
|
|||
*/
|
||||
@DomainModel(annotatedClasses = JsonMappingTests.EntityWithJson.class)
|
||||
@SessionFactory
|
||||
public class JsonMappingTests {
|
||||
public abstract class JsonMappingTests {
|
||||
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.JSON_FORMAT_MAPPER, value = "jsonb"))
|
||||
public static class JsonB extends JsonMappingTests {
|
||||
|
||||
public JsonB() {
|
||||
super( true );
|
||||
}
|
||||
}
|
||||
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.JSON_FORMAT_MAPPER, value = "jackson"))
|
||||
public static class Jackson extends JsonMappingTests {
|
||||
|
||||
public Jackson() {
|
||||
super( false );
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean supportsObjectMapKey;
|
||||
|
||||
protected JsonMappingTests(boolean supportsObjectMapKey) {
|
||||
this.supportsObjectMapKey = supportsObjectMapKey;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyMappings(SessionFactoryScope scope) {
|
||||
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( EntityWithJson.class);
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( EntityWithJson.class );
|
||||
final JdbcTypeRegistry jdbcTypeRegistry = mappingMetamodel.getTypeConfiguration().getJdbcTypeRegistry();
|
||||
|
||||
final BasicAttributeMapping duration = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("payload");
|
||||
final JdbcMapping jdbcMapping = duration.getJdbcMapping();
|
||||
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(Map.class));
|
||||
final JdbcType intervalType = jdbcTypeRegistry.getDescriptor(SqlTypes.JSON);
|
||||
final JdbcType realType;
|
||||
if (intervalType instanceof AdjustableJdbcType) {
|
||||
realType = ((AdjustableJdbcType) intervalType).resolveIndicatedType(
|
||||
() -> mappingMetamodel.getTypeConfiguration(),
|
||||
jdbcMapping.getJavaTypeDescriptor()
|
||||
);
|
||||
}
|
||||
else {
|
||||
realType = intervalType;
|
||||
}
|
||||
assertThat( jdbcMapping.getJdbcType(), is( realType));
|
||||
final BasicAttributeMapping payloadAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "payload" );
|
||||
final BasicAttributeMapping objectMapAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "objectMap" );
|
||||
final BasicAttributeMapping listAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "list" );
|
||||
|
||||
assertThat( payloadAttribute.getJavaType().getJavaTypeClass(), equalTo( Map.class ) );
|
||||
assertThat( objectMapAttribute.getJavaType().getJavaTypeClass(), equalTo( Map.class ) );
|
||||
assertThat( listAttribute.getJavaType().getJavaTypeClass(), equalTo( List.class ) );
|
||||
|
||||
final JdbcType jsonType = jdbcTypeRegistry.getDescriptor( SqlTypes.JSON );
|
||||
assertThat( payloadAttribute.getJdbcMapping().getJdbcType(), is( jsonType ) );
|
||||
assertThat( objectMapAttribute.getJdbcMapping().getJdbcType(), is( jsonType ) );
|
||||
assertThat( listAttribute.getJdbcMapping().getJdbcType(), is( jsonType ) );
|
||||
|
||||
Map<String, String> stringMap = Map.of( "name", "ABC" );
|
||||
Map<StringNode, StringNode> objectMap = supportsObjectMapKey ? Map.of( new StringNode( "name" ), new StringNode( "ABC" ) ) : null;
|
||||
List<StringNode> list = List.of( new StringNode( "ABC" ) );
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.persist( new EntityWithJson( 1, Map.of( "name", "ABC" ) ) );
|
||||
session.persist( new EntityWithJson( 1, stringMap, objectMap, list ) );
|
||||
}
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> session.find( EntityWithJson.class, 1)
|
||||
(session) -> {
|
||||
EntityWithJson entityWithJson = session.find( EntityWithJson.class, 1 );
|
||||
assertThat( entityWithJson.payload, is( stringMap ) );
|
||||
assertThat( entityWithJson.objectMap, is( objectMap ) );
|
||||
assertThat( entityWithJson.list, is( list ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -84,12 +114,62 @@ public class JsonMappingTests {
|
|||
private Map<String, String> payload;
|
||||
//end::basic-json-example[]
|
||||
|
||||
@JdbcTypeCode( SqlTypes.JSON )
|
||||
private Map<StringNode, StringNode> objectMap;
|
||||
|
||||
@JdbcTypeCode( SqlTypes.JSON )
|
||||
private List<StringNode> list;
|
||||
|
||||
public EntityWithJson() {
|
||||
}
|
||||
|
||||
public EntityWithJson(Integer id, Map<String, String> payload) {
|
||||
public EntityWithJson(
|
||||
Integer id,
|
||||
Map<String, String> payload,
|
||||
Map<StringNode, StringNode> objectMap,
|
||||
List<StringNode> list) {
|
||||
this.id = id;
|
||||
this.payload = payload;
|
||||
this.objectMap = objectMap;
|
||||
this.list = list;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringNode {
|
||||
private String string;
|
||||
|
||||
public StringNode() {
|
||||
}
|
||||
|
||||
public StringNode(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return string;
|
||||
}
|
||||
|
||||
public void setString(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StringNode that = (StringNode) o;
|
||||
|
||||
return string != null ? string.equals( that.string ) : that.string == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return string != null ? string.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.userguide.mapping.basic;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.xml.bind.annotation.XmlElement;
|
||||
import jakarta.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@DomainModel(annotatedClasses = XmlMappingTests.EntityWithXml.class)
|
||||
@SessionFactory
|
||||
public abstract class XmlMappingTests {
|
||||
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.XML_FORMAT_MAPPER, value = "jaxb"))
|
||||
public static class Jaxb extends XmlMappingTests {
|
||||
|
||||
public Jaxb() {
|
||||
super( true );
|
||||
}
|
||||
}
|
||||
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.XML_FORMAT_MAPPER, value = "jackson-xml"))
|
||||
public static class Jackson extends XmlMappingTests {
|
||||
|
||||
public Jackson() {
|
||||
super( false );
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean supportsObjectMapKey;
|
||||
|
||||
protected XmlMappingTests(boolean supportsObjectMapKey) {
|
||||
this.supportsObjectMapKey = supportsObjectMapKey;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyMappings(SessionFactoryScope scope) {
|
||||
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( EntityWithXml.class);
|
||||
final JdbcTypeRegistry jdbcTypeRegistry = mappingMetamodel.getTypeConfiguration().getJdbcTypeRegistry();
|
||||
|
||||
final BasicAttributeMapping stringMapAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "stringMap" );
|
||||
final BasicAttributeMapping objectMapAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "objectMap" );
|
||||
final BasicAttributeMapping listAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "list" );
|
||||
assertThat( stringMapAttribute.getJavaType().getJavaTypeClass(), equalTo( Map.class ) );
|
||||
assertThat( objectMapAttribute.getJavaType().getJavaTypeClass(), equalTo( Map.class ) );
|
||||
assertThat( listAttribute.getJavaType().getJavaTypeClass(), equalTo( List.class ) );
|
||||
|
||||
final JdbcType xmlType = jdbcTypeRegistry.getDescriptor(SqlTypes.SQLXML);
|
||||
assertThat( stringMapAttribute.getJdbcMapping().getJdbcType(), is( xmlType ) );
|
||||
assertThat( objectMapAttribute.getJdbcMapping().getJdbcType(), is( xmlType ) );
|
||||
assertThat( listAttribute.getJdbcMapping().getJdbcType(), is( xmlType ) );
|
||||
|
||||
Map<String, StringNode> stringMap = Map.of( "name", new StringNode( "ABC" ) );
|
||||
Map<StringNode, StringNode> objectMap = supportsObjectMapKey ? Map.of( new StringNode( "name" ), new StringNode( "ABC" ) ) : null;
|
||||
List<StringNode> list = List.of( new StringNode( "ABC" ) );
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.persist( new EntityWithXml( 1, stringMap, objectMap, list ) );
|
||||
}
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
EntityWithXml entityWithXml = session.find( EntityWithXml.class, 1 );
|
||||
assertThat( entityWithXml.stringMap, is( stringMap ) );
|
||||
assertThat( entityWithXml.objectMap, is( objectMap ) );
|
||||
assertThat( entityWithXml.list, is( list ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "EntityWithXml")
|
||||
@Table(name = "EntityWithXml")
|
||||
public static class EntityWithXml {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
//tag::basic-xml-example[]
|
||||
@JdbcTypeCode( SqlTypes.SQLXML )
|
||||
private Map<String, StringNode> stringMap;
|
||||
//end::basic-xml-example[]
|
||||
|
||||
@JdbcTypeCode( SqlTypes.SQLXML )
|
||||
private Map<StringNode, StringNode> objectMap;
|
||||
|
||||
@JdbcTypeCode( SqlTypes.SQLXML )
|
||||
private List<StringNode> list;
|
||||
|
||||
public EntityWithXml() {
|
||||
}
|
||||
|
||||
public EntityWithXml(
|
||||
Integer id,
|
||||
Map<String, StringNode> stringMap,
|
||||
Map<StringNode, StringNode> objectMap,
|
||||
List<StringNode> list) {
|
||||
this.id = id;
|
||||
this.stringMap = stringMap;
|
||||
this.objectMap = objectMap;
|
||||
this.list = list;
|
||||
}
|
||||
}
|
||||
|
||||
@XmlRootElement(name = "stringNode")
|
||||
public static class StringNode {
|
||||
private String string;
|
||||
|
||||
public StringNode() {
|
||||
}
|
||||
|
||||
public StringNode(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@XmlElement
|
||||
public String getString() {
|
||||
return string;
|
||||
}
|
||||
|
||||
public void setString(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StringNode that = (StringNode) o;
|
||||
|
||||
return string != null ? string.equals( that.string ) : that.string == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return string != null ? string.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -87,6 +87,8 @@ dependencies {
|
|||
testRuntimeOnly dbLibs.informix
|
||||
testRuntimeOnly dbLibs.cockroachdb
|
||||
testRuntimeOnly dbLibs.oracle
|
||||
testRuntimeOnly dbLibs.oracleXml
|
||||
testRuntimeOnly dbLibs.oracleXmlParser
|
||||
testRuntimeOnly dbLibs.sybase
|
||||
|
||||
// Since both the DB2 driver and HANA have a package "net.jpountz" we have to add dependencies conditionally
|
||||
|
|
|
@ -42,8 +42,9 @@ dependencies {
|
|||
compileOnly jakartaLibs.jacc
|
||||
compileOnly jakartaLibs.validation
|
||||
compileOnly jakartaLibs.cdi
|
||||
compileOnly jakartaLibs.jsonb
|
||||
compileOnly jakartaLibs.jsonbApi
|
||||
compileOnly libs.jackson
|
||||
compileOnly libs.jacksonXml
|
||||
|
||||
testImplementation project(':hibernate-testing')
|
||||
testImplementation project(':hibernate-ant')
|
||||
|
|
|
@ -72,6 +72,8 @@ import org.hibernate.service.spi.ServiceRegistryImplementor;
|
|||
import org.hibernate.stat.Statistics;
|
||||
import org.hibernate.type.FormatMapper;
|
||||
import org.hibernate.type.JacksonJsonFormatMapper;
|
||||
import org.hibernate.type.JacksonXmlFormatMapper;
|
||||
import org.hibernate.type.JaxbXmlFormatMapper;
|
||||
import org.hibernate.type.JsonBJsonFormatMapper;
|
||||
|
||||
import static org.hibernate.cfg.AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS;
|
||||
|
@ -151,6 +153,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
private Object beanManagerReference;
|
||||
private Object validatorFactoryReference;
|
||||
private FormatMapper jsonFormatMapper;
|
||||
private FormatMapper xmlFormatMapper;
|
||||
|
||||
// SessionFactory behavior
|
||||
private boolean jpaBootstrap;
|
||||
|
@ -298,6 +301,10 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
configurationSettings.get( AvailableSettings.JSON_FORMAT_MAPPER ),
|
||||
strategySelector
|
||||
);
|
||||
this.xmlFormatMapper = determineXmlFormatMapper(
|
||||
configurationSettings.get( AvailableSettings.XML_FORMAT_MAPPER ),
|
||||
strategySelector
|
||||
);
|
||||
|
||||
this.sessionFactoryName = (String) configurationSettings.get( SESSION_FACTORY_NAME );
|
||||
this.sessionFactoryNameAlsoJndiName = cfgService.getSetting(
|
||||
|
@ -809,6 +816,32 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
);
|
||||
}
|
||||
|
||||
private static FormatMapper determineXmlFormatMapper(Object setting, StrategySelector strategySelector) {
|
||||
return strategySelector.resolveDefaultableStrategy(
|
||||
FormatMapper.class,
|
||||
setting,
|
||||
(Callable<FormatMapper>) () -> {
|
||||
try {
|
||||
// Force initialization of the instance
|
||||
JacksonXmlFormatMapper.INSTANCE.hashCode();
|
||||
return JacksonXmlFormatMapper.INSTANCE;
|
||||
}
|
||||
catch (NoClassDefFoundError ex) {
|
||||
// Ignore
|
||||
}
|
||||
try {
|
||||
// Force initialization of the instance
|
||||
JaxbXmlFormatMapper.INSTANCE.hashCode();
|
||||
return JaxbXmlFormatMapper.INSTANCE;
|
||||
}
|
||||
catch (NoClassDefFoundError ex) {
|
||||
// Ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// SessionFactoryOptionsState
|
||||
|
@ -1218,7 +1251,13 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
public FormatMapper getJsonFormatMapper() {
|
||||
return jsonFormatMapper;
|
||||
}
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@Override
|
||||
public FormatMapper getXmlFormatMapper() {
|
||||
return xmlFormatMapper;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// In-flight mutation access
|
||||
|
||||
public void applyBeanManager(Object beanManager) {
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.hibernate.type.SqlTypes;
|
|||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.XmlAsStringJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.DdlType;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
|
@ -387,6 +388,7 @@ public class MetadataBuildingProcess {
|
|||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.UUID, SqlTypes.BINARY );
|
||||
}
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( JsonJdbcType.INSTANCE );
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( XmlAsStringJdbcType.INSTANCE );
|
||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY );
|
||||
final int preferredSqlTypeCodeForDuration = ConfigurationHelper.getPreferredSqlTypeCodeForDuration( bootstrapContext.getServiceRegistry() );
|
||||
if ( preferredSqlTypeCodeForDuration != SqlTypes.INTERVAL_SECOND ) {
|
||||
|
@ -408,6 +410,13 @@ public class MetadataBuildingProcess {
|
|||
dialect
|
||||
)
|
||||
);
|
||||
ddlTypeRegistry.addDescriptorIfAbsent(
|
||||
new DdlTypeImpl(
|
||||
SqlTypes.SQLXML,
|
||||
ddlTypeRegistry.getTypeName( SqlTypes.VARCHAR, null, null, null ),
|
||||
dialect
|
||||
)
|
||||
);
|
||||
// Fallback to the geometry DdlType when geography is requested
|
||||
final DdlType geometryType = ddlTypeRegistry.getDescriptor( SqlTypes.GEOMETRY );
|
||||
if ( geometryType != null ) {
|
||||
|
|
|
@ -40,6 +40,8 @@ import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoo
|
|||
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
|
||||
import org.hibernate.type.FormatMapper;
|
||||
import org.hibernate.type.JacksonJsonFormatMapper;
|
||||
import org.hibernate.type.JacksonXmlFormatMapper;
|
||||
import org.hibernate.type.JaxbXmlFormatMapper;
|
||||
import org.hibernate.type.JsonBJsonFormatMapper;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -114,6 +116,7 @@ public class StrategySelectorBuilder {
|
|||
addImplicitNamingStrategies( strategySelector );
|
||||
addCacheKeysFactories( strategySelector );
|
||||
addJsonFormatMappers( strategySelector );
|
||||
addXmlFormatMappers( strategySelector );
|
||||
|
||||
// apply auto-discovered registrations
|
||||
for ( StrategyRegistrationProvider provider : classLoaderService.loadJavaServices( StrategyRegistrationProvider.class ) ) {
|
||||
|
@ -265,4 +268,17 @@ public class StrategySelectorBuilder {
|
|||
JsonBJsonFormatMapper.class
|
||||
);
|
||||
}
|
||||
|
||||
private void addXmlFormatMappers(StrategySelectorImpl strategySelector) {
|
||||
strategySelector.registerStrategyImplementor(
|
||||
FormatMapper.class,
|
||||
JacksonXmlFormatMapper.SHORT_NAME,
|
||||
JacksonXmlFormatMapper.class
|
||||
);
|
||||
strategySelector.registerStrategyImplementor(
|
||||
FormatMapper.class,
|
||||
JaxbXmlFormatMapper.SHORT_NAME,
|
||||
JaxbXmlFormatMapper.class
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -461,4 +461,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
|
|||
public FormatMapper getJsonFormatMapper() {
|
||||
return delegate.getJsonFormatMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormatMapper getXmlFormatMapper() {
|
||||
return delegate.getXmlFormatMapper();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -314,4 +314,11 @@ public interface SessionFactoryOptions extends QueryEngineOptions {
|
|||
TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy();
|
||||
|
||||
FormatMapper getJsonFormatMapper();
|
||||
|
||||
/**
|
||||
* The format mapper to use for serializing/deserializing XML data.
|
||||
*
|
||||
* @since 6.0.1
|
||||
*/
|
||||
FormatMapper getXmlFormatMapper();
|
||||
}
|
||||
|
|
|
@ -2562,15 +2562,31 @@ public interface AvailableSettings {
|
|||
* <li>an instance of {@code FormatMapper},
|
||||
* <li>a {@link Class} representing a class that implements {@code FormatMapper},
|
||||
* <li>the name of a class that implements {@code FormatMapper}, or
|
||||
* <li>one of the short hand constants {@code jackson} or {@code jsonb}.
|
||||
* <li>one of the shorthand constants {@code jackson} or {@code jsonb}.
|
||||
* </ul>
|
||||
* By default, the first of the possible providers that is available in the runtime is
|
||||
* By default, the first of the possible providers that is available at runtime is
|
||||
* used, according to the listing order.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
String JSON_FORMAT_MAPPER = "hibernate.type.json_format_mapper";
|
||||
|
||||
/**
|
||||
* Specifies a {@link org.hibernate.type.FormatMapper} used for XML serialization
|
||||
* and deserialization, either:
|
||||
* <ul>
|
||||
* <li>an instance of {@code FormatMapper},
|
||||
* <li>a {@link Class} representing a class that implements {@code FormatMapper},
|
||||
* <li>the name of a class that implements {@code FormatMapper}, or
|
||||
* <li>one of the shorthand constants {@code jackson} or {@code jaxb}.
|
||||
* </ul>
|
||||
* By default, the first of the possible providers that is available at runtime is
|
||||
* used, according to the listing order.
|
||||
*
|
||||
* @since 6.0.1
|
||||
*/
|
||||
String XML_FORMAT_MAPPER = "hibernate.type.xml_format_mapper";
|
||||
|
||||
/**
|
||||
* Specifies the default strategy for storage of the timezone information
|
||||
* for zoned datetime types:
|
||||
|
|
|
@ -306,7 +306,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
|
||||
// If we get into this method we know that there is a Java type for the value
|
||||
// and that it is safe to load on the app classloader.
|
||||
final Class<?> modelJavaType = resolveJavaType( modelTypeXClass );
|
||||
final java.lang.reflect.Type modelJavaType = resolveJavaType( modelTypeXClass );
|
||||
if ( modelJavaType == null ) {
|
||||
throw new IllegalStateException( "BasicType requires Java type" );
|
||||
}
|
||||
|
@ -495,9 +495,10 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
else {
|
||||
mapKeyClass = modelPropertyTypeXClass;
|
||||
}
|
||||
final Class<?> implicitJavaType = resolveJavaType( mapKeyClass );
|
||||
final java.lang.reflect.Type javaType = resolveJavaType( mapKeyClass );
|
||||
final Class<Object> javaTypeClass = ReflectHelper.getClass( javaType );
|
||||
|
||||
implicitJavaTypeAccess = (typeConfiguration) -> implicitJavaType;
|
||||
implicitJavaTypeAccess = (typeConfiguration) -> javaType;
|
||||
|
||||
final MapKeyEnumerated mapKeyEnumeratedAnn = mapAttribute.getAnnotation( MapKeyEnumerated.class );
|
||||
if ( mapKeyEnumeratedAnn != null ) {
|
||||
|
@ -638,7 +639,8 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
XClass xclass = elementTypeXClass == null && attributeXProperty.isArray()
|
||||
? attributeXProperty.getElementClass()
|
||||
: elementTypeXClass;
|
||||
Class<?> javaType = resolveJavaType( xclass );
|
||||
final java.lang.reflect.Type javaType = resolveJavaType( xclass );
|
||||
final Class<Object> javaTypeClass = ReflectHelper.getClass( javaType );
|
||||
|
||||
implicitJavaTypeAccess = typeConfiguration -> javaType;
|
||||
|
||||
|
@ -657,7 +659,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
temporalPrecision = null;
|
||||
}
|
||||
|
||||
if ( javaType.isEnum() ) {
|
||||
if ( javaTypeClass.isEnum() ) {
|
||||
final Enumerated enumeratedAnn = attributeXProperty.getAnnotation( Enumerated.class );
|
||||
if ( enumeratedAnn != null ) {
|
||||
enumType = enumeratedAnn.value();
|
||||
|
@ -697,7 +699,8 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
|
||||
private void prepareBasicAttribute(String declaringClassName, XProperty attributeDescriptor, XClass attributeType) {
|
||||
|
||||
final Class<?> javaType = resolveJavaType( attributeType );
|
||||
final java.lang.reflect.Type javaType = resolveJavaType( attributeType );
|
||||
final Class<Object> javaTypeClass = ReflectHelper.getClass( javaType );
|
||||
|
||||
implicitJavaTypeAccess = typeConfiguration -> javaType;
|
||||
|
||||
|
@ -716,7 +719,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
this.temporalPrecision = null;
|
||||
}
|
||||
|
||||
if ( javaType.isEnum() ) {
|
||||
if ( javaTypeClass.isEnum() ) {
|
||||
final Enumerated enumeratedAnn = attributeDescriptor.getAnnotation( Enumerated.class );
|
||||
if ( enumeratedAnn != null ) {
|
||||
this.enumType = enumeratedAnn.value();
|
||||
|
@ -976,10 +979,10 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
return mutability;
|
||||
}
|
||||
|
||||
private Class<?> resolveJavaType(XClass returnedClassOrElement) {
|
||||
private java.lang.reflect.Type resolveJavaType(XClass returnedClassOrElement) {
|
||||
return buildingContext.getBootstrapContext()
|
||||
.getReflectionManager()
|
||||
.toClass( returnedClassOrElement );
|
||||
.getReflectionManager()
|
||||
.toType( returnedClassOrElement );
|
||||
}
|
||||
|
||||
private Dialect getDialect() {
|
||||
|
|
|
@ -56,6 +56,7 @@ import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
|||
import org.hibernate.type.descriptor.jdbc.*;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
|
@ -163,6 +164,8 @@ public class DB2Dialect extends Dialect {
|
|||
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) );
|
||||
|
||||
if ( getDB2Version().isBefore( 11 ) ) {
|
||||
// should use 'binary' since version 11
|
||||
ddlTypeRegistry.addDescriptor(
|
||||
|
@ -638,6 +641,8 @@ public class DB2Dialect extends Dialect {
|
|||
jdbcTypeRegistry.addDescriptor( Types.NVARCHAR, VarcharJdbcType.INSTANCE );
|
||||
jdbcTypeRegistry.addDescriptor( Types.NUMERIC, DecimalJdbcType.INSTANCE );
|
||||
|
||||
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
|
||||
|
||||
// DB2 requires a custom binder for binding untyped nulls that resolves the type through the statement
|
||||
typeContributions.contributeJdbcType( ObjectNullResolvingJdbcType.INSTANCE );
|
||||
|
||||
|
|
|
@ -599,6 +599,7 @@ public class OracleDialect extends Dialect {
|
|||
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "SYS.XMLTYPE", this ) );
|
||||
if ( getVersion().isSameOrAfter( 10 ) ) {
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
||||
}
|
||||
|
@ -686,6 +687,8 @@ public class OracleDialect extends Dialect {
|
|||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.contributeTypes( typeContributions, serviceRegistry );
|
||||
|
||||
typeContributions.contributeJdbcType( OracleXmlJdbcType.INSTANCE );
|
||||
|
||||
if ( getVersion().isSameOrAfter( 12 ) ) {
|
||||
// account for Oracle's deprecated support for LONGVARBINARY
|
||||
// prefer BLOB, unless the user explicitly opts out
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class OracleXmlJdbcType extends XmlJdbcType {
|
||||
|
||||
public static final OracleXmlJdbcType INSTANCE = new OracleXmlJdbcType();
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
// Seems the Oracle JDBC driver doesn't support `setNull(index, Types.SQLXML)`
|
||||
// but it seems that the following works fine
|
||||
return new XmlValueBinder<>( javaType, this ) {
|
||||
@Override
|
||||
protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException {
|
||||
st.setNull( index, Types.VARCHAR );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException {
|
||||
st.setNull( name, Types.VARCHAR );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -72,6 +72,7 @@ import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType
|
|||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType;
|
||||
|
@ -99,6 +100,7 @@ import static org.hibernate.type.SqlTypes.NCHAR;
|
|||
import static org.hibernate.type.SqlTypes.NCLOB;
|
||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.OTHER;
|
||||
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||
|
@ -207,6 +209,7 @@ public class PostgreSQLDialect extends Dialect {
|
|||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) );
|
||||
ddlTypeRegistry.addDescriptor( new Scale6IntervalSecondDdlType( this ) );
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) );
|
||||
|
||||
if ( getVersion().isSameOrAfter( 8, 2 ) ) {
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uuid", this ) );
|
||||
|
@ -255,6 +258,9 @@ public class PostgreSQLDialect extends Dialect {
|
|||
case "jsonb":
|
||||
jdbcTypeCode = JSON;
|
||||
break;
|
||||
case "xml":
|
||||
jdbcTypeCode = SQLXML;
|
||||
break;
|
||||
case "inet":
|
||||
jdbcTypeCode = INET;
|
||||
break;
|
||||
|
@ -1094,6 +1100,7 @@ public class PostgreSQLDialect extends Dialect {
|
|||
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.BLOB_BINDING );
|
||||
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
|
||||
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantAsTimestampWithTimeZoneJdbcType.INSTANCE );
|
||||
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
|
||||
|
||||
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLInetJdbcType.INSTANCE );
|
||||
|
|
|
@ -129,27 +129,26 @@ public abstract class PostgreSQLPGObjectJdbcType implements JdbcType {
|
|||
return new BasicExtractor<>( javaType, this ) {
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return ( (PostgreSQLPGObjectJdbcType) getJdbcType() ).fromString(
|
||||
rs.getString( paramIndex ),
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
return getObject( rs.getString( paramIndex ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||
return ( (PostgreSQLPGObjectJdbcType) getJdbcType() ).fromString(
|
||||
statement.getString( index ),
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
return getObject( statement.getString( index ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return getObject( statement.getString( name ), options );
|
||||
}
|
||||
|
||||
private X getObject(String string, WrapperOptions options) throws SQLException {
|
||||
if ( string == null ) {
|
||||
return null;
|
||||
}
|
||||
return ( (PostgreSQLPGObjectJdbcType) getJdbcType() ).fromString(
|
||||
statement.getString( name ),
|
||||
string,
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
|
|
|
@ -54,6 +54,7 @@ import org.hibernate.type.BasicTypeRegistry;
|
|||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
|
||||
|
@ -173,6 +174,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) );
|
||||
}
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -211,6 +213,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
Types.TINYINT,
|
||||
SmallIntJdbcType.INSTANCE
|
||||
);
|
||||
typeContributions.contributeJdbcType( XmlJdbcType.INSTANCE );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -169,6 +169,7 @@ public final class FastSessionServices {
|
|||
private final CacheRetrieveMode defaultCacheRetrieveMode;
|
||||
private final ConnectionObserverStatsBridge defaultJdbcObservers;
|
||||
private final FormatMapper jsonFormatMapper;
|
||||
private final FormatMapper xmlFormatMapper;
|
||||
|
||||
FastSessionServices(SessionFactoryImpl sf) {
|
||||
Objects.requireNonNull( sf );
|
||||
|
@ -243,6 +244,7 @@ public final class FastSessionServices {
|
|||
this.defaultLockOptions = initializeDefaultLockOptions( defaultSessionProperties );
|
||||
this.initialSessionFlushMode = initializeDefaultFlushMode( defaultSessionProperties );
|
||||
this.jsonFormatMapper = sessionFactoryOptions.getJsonFormatMapper();
|
||||
this.xmlFormatMapper = sessionFactoryOptions.getXmlFormatMapper();
|
||||
}
|
||||
|
||||
private static FlushMode initializeDefaultFlushMode(Map<String, Object> defaultSessionProperties) {
|
||||
|
@ -374,4 +376,8 @@ public final class FastSessionServices {
|
|||
public FormatMapper getJsonFormatMapper() {
|
||||
return jsonFormatMapper;
|
||||
}
|
||||
|
||||
public FormatMapper getXmlFormatMapper() {
|
||||
return xmlFormatMapper;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class JacksonXmlFormatMapper implements FormatMapper {
|
||||
|
||||
public static final String SHORT_NAME = "jackson-xml";
|
||||
public static final JacksonXmlFormatMapper INSTANCE = new JacksonXmlFormatMapper();
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public JacksonXmlFormatMapper() {
|
||||
this(new XmlMapper());
|
||||
}
|
||||
|
||||
public JacksonXmlFormatMapper(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||
try {
|
||||
return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( javaType.getJavaType() ) );
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||
try {
|
||||
return objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) )
|
||||
.writeValueAsString( value );
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType, e );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import jakarta.xml.bind.Marshaller;
|
||||
import jakarta.xml.bind.Unmarshaller;
|
||||
import jakarta.xml.bind.annotation.XmlAnyElement;
|
||||
import jakarta.xml.bind.annotation.XmlRootElement;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class JaxbXmlFormatMapper implements FormatMapper {
|
||||
|
||||
public static final String SHORT_NAME = "jaxb";
|
||||
public static final JaxbXmlFormatMapper INSTANCE = new JaxbXmlFormatMapper();
|
||||
|
||||
public JaxbXmlFormatMapper() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||
try {
|
||||
if ( Map.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
|
||||
final JAXBContext context;
|
||||
final Class<Object> keyClass;
|
||||
final Class<Object> valueClass;
|
||||
if ( javaType.getJavaType() instanceof ParameterizedType ) {
|
||||
final Type[] typeArguments = ( (ParameterizedType) javaType.getJavaType() ).getActualTypeArguments();
|
||||
keyClass = ReflectHelper.getClass( typeArguments[0] );
|
||||
valueClass = ReflectHelper.getClass( typeArguments[1] );
|
||||
context = JAXBContext.newInstance( MapWrapper.class, keyClass, valueClass );
|
||||
}
|
||||
else {
|
||||
keyClass = Object.class;
|
||||
valueClass = Object.class;
|
||||
context = JAXBContext.newInstance( MapWrapper.class );
|
||||
}
|
||||
final Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
final MapWrapper mapWrapper = (MapWrapper) unmarshaller
|
||||
.unmarshal( new StringReader( charSequence.toString() ) );
|
||||
final List<Object> elements = mapWrapper.elements;
|
||||
final Map<Object, Object> map = CollectionHelper.linkedMapOfSize( elements.size() >> 1 );
|
||||
for ( int i = 0; i < elements.size(); i += 2 ) {
|
||||
final Object keyElement = unmarshaller.unmarshal( (Node) elements.get( i ), keyClass ).getValue();
|
||||
final Object valueElement = unmarshaller.unmarshal( (Node) elements.get( i + 1 ), valueClass )
|
||||
.getValue();
|
||||
final Object key;
|
||||
final Object value;
|
||||
if ( keyElement instanceof Element ) {
|
||||
key = ( (Element) keyElement ).getFirstChild().getTextContent();
|
||||
}
|
||||
else {
|
||||
key = keyElement;
|
||||
}
|
||||
if ( valueElement instanceof Element ) {
|
||||
value = ( (Element) valueElement ).getFirstChild().getTextContent();
|
||||
}
|
||||
else {
|
||||
value = valueElement;
|
||||
}
|
||||
map.put( key, value );
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) map;
|
||||
}
|
||||
else if ( Collection.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
|
||||
final JAXBContext context;
|
||||
final Class<Object> valueClass;
|
||||
if ( javaType.getJavaType() instanceof ParameterizedType ) {
|
||||
final Type[] typeArguments = ( (ParameterizedType) javaType.getJavaType() ).getActualTypeArguments();
|
||||
valueClass = ReflectHelper.getClass( typeArguments[0] );
|
||||
context = JAXBContext.newInstance( CollectionWrapper.class, valueClass );
|
||||
}
|
||||
else {
|
||||
valueClass = Object.class;
|
||||
context = JAXBContext.newInstance( CollectionWrapper.class );
|
||||
}
|
||||
final Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
final CollectionWrapper collectionWrapper = (CollectionWrapper) unmarshaller
|
||||
.unmarshal( new StringReader( charSequence.toString() ) );
|
||||
final List<Object> elements = collectionWrapper.elements;
|
||||
final Collection<Object> collection = new ArrayList<>( elements.size() >> 1 );
|
||||
for ( int i = 0; i < elements.size(); i++ ) {
|
||||
final Object valueElement = unmarshaller.unmarshal( (Node) elements.get( i ), valueClass )
|
||||
.getValue();
|
||||
final Object value;
|
||||
if ( valueElement instanceof Element ) {
|
||||
value = ( (Element) valueElement ).getFirstChild().getTextContent();
|
||||
}
|
||||
else {
|
||||
value = valueElement;
|
||||
}
|
||||
collection.add( value );
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) collection;
|
||||
}
|
||||
else {
|
||||
final JAXBContext context = JAXBContext.newInstance( javaType.getJavaTypeClass() );
|
||||
//noinspection unchecked
|
||||
return (T) context.createUnmarshaller().unmarshal( new StringReader( charSequence.toString() ) );
|
||||
}
|
||||
}
|
||||
catch (JAXBException e) {
|
||||
throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||
try {
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
if ( Map.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
|
||||
final JAXBContext context;
|
||||
final Class<Object> keyClass;
|
||||
final Class<Object> valueClass;
|
||||
final MapWrapper mapWrapper = new MapWrapper();
|
||||
final Map<?, ?> map = (Map<?, ?>) value;
|
||||
if ( javaType.getJavaType() instanceof ParameterizedType ) {
|
||||
final Type[] typeArguments = ( (ParameterizedType) javaType.getJavaType() ).getActualTypeArguments();
|
||||
keyClass = ReflectHelper.getClass( typeArguments[0] );
|
||||
valueClass = ReflectHelper.getClass( typeArguments[1] );
|
||||
context = JAXBContext.newInstance( MapWrapper.class, keyClass, valueClass );
|
||||
}
|
||||
else {
|
||||
if ( map.isEmpty() ) {
|
||||
keyClass = Object.class;
|
||||
valueClass = Object.class;
|
||||
context = JAXBContext.newInstance( MapWrapper.class );
|
||||
}
|
||||
else {
|
||||
final Map.Entry<?, ?> firstEntry = map.entrySet().iterator().next();
|
||||
//noinspection unchecked
|
||||
keyClass = (Class<Object>) firstEntry.getKey().getClass();
|
||||
//noinspection unchecked
|
||||
valueClass = (Class<Object>) firstEntry.getValue().getClass();
|
||||
context = JAXBContext.newInstance( MapWrapper.class, keyClass, valueClass );
|
||||
}
|
||||
}
|
||||
for ( Map.Entry<?, ?> entry : map.entrySet() ) {
|
||||
mapWrapper.elements.add(
|
||||
new JAXBElement<>(
|
||||
new QName( "key" ),
|
||||
keyClass,
|
||||
entry.getKey()
|
||||
)
|
||||
);
|
||||
mapWrapper.elements.add(
|
||||
new JAXBElement<>(
|
||||
new QName( "value" ),
|
||||
valueClass,
|
||||
entry.getValue()
|
||||
)
|
||||
);
|
||||
}
|
||||
createMarshaller( context ).marshal( mapWrapper, stringWriter );
|
||||
}
|
||||
else if ( Collection.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
|
||||
final JAXBContext context;
|
||||
final Class<Object> valueClass;
|
||||
final Collection<?> collection = (Collection<?>) value;
|
||||
final CollectionWrapper collectionWrapper = new CollectionWrapper( new ArrayList<>( collection ) );
|
||||
if ( javaType.getJavaType() instanceof ParameterizedType ) {
|
||||
final Type[] typeArguments = ( (ParameterizedType) javaType.getJavaType() ).getActualTypeArguments();
|
||||
valueClass = ReflectHelper.getClass( typeArguments[0] );
|
||||
context = JAXBContext.newInstance( CollectionWrapper.class, valueClass );
|
||||
}
|
||||
else {
|
||||
if ( collection.isEmpty() ) {
|
||||
valueClass = Object.class;
|
||||
context = JAXBContext.newInstance( CollectionWrapper.class );
|
||||
}
|
||||
else {
|
||||
//noinspection unchecked
|
||||
valueClass = (Class<Object>) collection.iterator().next().getClass();
|
||||
context = JAXBContext.newInstance( CollectionWrapper.class, valueClass );
|
||||
}
|
||||
}
|
||||
// for ( Object element : collection ) {
|
||||
// collectionWrapper.elements.add(
|
||||
// new JAXBElement<>(
|
||||
// new QName( "key" ),
|
||||
// keyClass,
|
||||
// entry.getKey()
|
||||
// )
|
||||
// );
|
||||
// collectionWrapper.elements.add(
|
||||
// new JAXBElement<>(
|
||||
// new QName( "value" ),
|
||||
// valueClass,
|
||||
// entry.getValue()
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
createMarshaller( context ).marshal( collectionWrapper, stringWriter );
|
||||
}
|
||||
else {
|
||||
final JAXBContext context = JAXBContext.newInstance( javaType.getJavaTypeClass() );
|
||||
createMarshaller( context ).marshal( value, stringWriter );
|
||||
}
|
||||
return stringWriter.toString();
|
||||
}
|
||||
catch (JAXBException e) {
|
||||
throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType, e );
|
||||
}
|
||||
}
|
||||
|
||||
private Marshaller createMarshaller(JAXBContext context) throws JAXBException {
|
||||
final Marshaller marshaller = context.createMarshaller();
|
||||
marshaller.setProperty( Marshaller.JAXB_FRAGMENT, true );
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
@XmlRootElement(name = "Map")
|
||||
public static class MapWrapper {
|
||||
@XmlAnyElement
|
||||
List<Object> elements = new ArrayList<>();
|
||||
}
|
||||
|
||||
@XmlRootElement(name = "Collection")
|
||||
public static class CollectionWrapper {
|
||||
@XmlAnyElement
|
||||
List<Object> elements = new ArrayList<>();
|
||||
|
||||
public CollectionWrapper() {
|
||||
}
|
||||
|
||||
public CollectionWrapper(List<Object> elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -461,6 +461,8 @@ public class SqlTypes {
|
|||
* The constant in the Java programming language, sometimes referred to
|
||||
* as a type code, that identifies the generic SQL type
|
||||
* {@code GEOGRAPHY}.
|
||||
*
|
||||
* @since 6.0.1
|
||||
*/
|
||||
public static final int GEOGRAPHY = 3250;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.type.descriptor.java;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
@ -248,4 +249,12 @@ public interface JavaType<T> extends Serializable {
|
|||
default String getCheckCondition(String columnName, JdbcType sqlType, Dialect dialect) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link JavaType} for the given {@link ParameterizedType} based on this {@link JavaType} registered
|
||||
* for the raw type.
|
||||
*/
|
||||
default JavaType<T> createJavaType(ParameterizedType parameterizedType) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,15 @@
|
|||
*/
|
||||
package org.hibernate.type.descriptor.java.spi;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
|
||||
import org.hibernate.collection.spi.CollectionSemantics;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
|
||||
|
@ -41,6 +46,13 @@ public class CollectionJavaType<C> extends AbstractClassJavaType<C> {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<C> createJavaType(ParameterizedType parameterizedType) {
|
||||
//noinspection unchecked
|
||||
// Construct a basic java type that knows its parametrization
|
||||
return new UnknownBasicJavaType<>( parameterizedType, (MutabilityPlan<C>) MutableMutabilityPlan.INSTANCE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public C fromString(CharSequence string) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -132,17 +132,15 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
|
|||
return resolveDescriptor(
|
||||
javaType,
|
||||
() -> {
|
||||
final Class<?> javaTypeClass;
|
||||
if ( javaType instanceof Class<?> ) {
|
||||
javaTypeClass = (Class<?>) javaType;
|
||||
}
|
||||
else {
|
||||
if ( javaType instanceof ParameterizedType ) {
|
||||
final ParameterizedType parameterizedType = (ParameterizedType) javaType;
|
||||
javaTypeClass = (Class<?>) parameterizedType.getRawType();
|
||||
final JavaType<J> rawType = findDescriptor( ( parameterizedType ).getRawType() );
|
||||
if ( rawType != null ) {
|
||||
return rawType.createJavaType( parameterizedType );
|
||||
}
|
||||
}
|
||||
|
||||
return RegistryHelper.INSTANCE.createTypeDescriptor(
|
||||
javaTypeClass,
|
||||
javaType,
|
||||
() -> {
|
||||
final MutabilityPlan<J> determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan( javaType, typeConfiguration );
|
||||
if ( determinedPlan != null ) {
|
||||
|
|
|
@ -102,7 +102,7 @@ public class RegistryHelper {
|
|||
return new SerializableJavaType( javaTypeClass, plan );
|
||||
}
|
||||
|
||||
return new JavaTypeBasicAdaptor<>( javaTypeClass, plan );
|
||||
return new UnknownBasicJavaType<>( javaType, plan );
|
||||
}
|
||||
|
||||
private <J> Class<J> determineJavaTypeClass(Type javaType) {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.type.descriptor.java.spi;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.AbstractJavaType;
|
||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
|
||||
/**
|
||||
* AbstractBasicTypeDescriptor adapter for cases where we do not know a proper JavaType
|
||||
* for a given Java type.
|
||||
*/
|
||||
public class UnknownBasicJavaType<T> extends AbstractJavaType<T> {
|
||||
public UnknownBasicJavaType(Class<T> type) {
|
||||
super( type );
|
||||
}
|
||||
|
||||
public UnknownBasicJavaType(Class<T> type, MutabilityPlan<T> mutabilityPlan) {
|
||||
super( type, mutabilityPlan );
|
||||
}
|
||||
|
||||
public UnknownBasicJavaType(Type type, MutabilityPlan<T> mutabilityPlan) {
|
||||
super( type, mutabilityPlan );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
|
||||
throw new JdbcTypeRecommendationException(
|
||||
"Could not determine recommended JdbcType for `" + getJavaType().getTypeName() + "`"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(T value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fromString(CharSequence string) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Conversion from String strategy not known for this Java type : " + getJavaType().getTypeName()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(T value, Class<X> type, WrapperOptions options) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Unwrap strategy not known for this Java type : " + getJavaType().getTypeName()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> T wrap(X value, WrapperOptions options) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Wrap strategy not known for this Java type : " + getJavaType().getTypeName()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BasicJavaType(" + getJavaType().getTypeName() + ")";
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ public class JsonJdbcType implements JdbcType {
|
|||
|
||||
@Override
|
||||
public int getJdbcTypeCode() {
|
||||
return SqlTypes.OTHER;
|
||||
return SqlTypes.VARCHAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,26 +75,25 @@ public class JsonJdbcType implements JdbcType {
|
|||
return new BasicExtractor<>( javaType, this ) {
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().fromString(
|
||||
rs.getString( paramIndex ),
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
return getObject( rs.getString( paramIndex ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||
return options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().fromString(
|
||||
statement.getString( index ),
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
return getObject( statement.getString( index ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||
return getObject( statement.getString( name ), options );
|
||||
}
|
||||
|
||||
private X getObject(String json, WrapperOptions options) throws SQLException {
|
||||
if ( json == null ) {
|
||||
return null;
|
||||
}
|
||||
return options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().fromString(
|
||||
statement.getString( name ),
|
||||
json,
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.type.descriptor.jdbc;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
/**
|
||||
* Specialized type mapping for {@code SQLXML} and the XML SQL data type.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class XmlAsStringJdbcType implements JdbcType {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final XmlAsStringJdbcType INSTANCE = new XmlAsStringJdbcType();
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCode() {
|
||||
return SqlTypes.VARCHAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultSqlTypeCode() {
|
||||
return SqlTypes.SQLXML;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "XmlAsStringJdbcType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
@Override
|
||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
final String xml = options.getSessionFactory().getFastSessionServices().getXmlFormatMapper().toString(
|
||||
value,
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
st.setString( index, xml );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
final String xml = options.getSessionFactory().getFastSessionServices().getXmlFormatMapper().toString(
|
||||
value,
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
st.setString( name, xml );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
|
||||
return new BasicExtractor<>( javaType, this ) {
|
||||
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return getObject( rs.getString( paramIndex ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||
return getObject( statement.getString( index ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||
return getObject( statement.getString( name ), options );
|
||||
}
|
||||
|
||||
private X getObject(String xml, WrapperOptions options) throws SQLException {
|
||||
if ( xml == null ) {
|
||||
return null;
|
||||
}
|
||||
return options.getSessionFactory().getFastSessionServices().getXmlFormatMapper().fromString(
|
||||
xml,
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.type.descriptor.jdbc;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLXML;
|
||||
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
/**
|
||||
* Specialized type mapping for {@code SQLXML} and the XML SQL data type.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class XmlJdbcType implements JdbcType {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final XmlJdbcType INSTANCE = new XmlJdbcType();
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCode() {
|
||||
return SqlTypes.SQLXML;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "XmlJdbcType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
return new XmlValueBinder<>( javaType, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
|
||||
return new BasicExtractor<>( javaType, this ) {
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return getObject( rs.getSQLXML( paramIndex ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||
return getObject( statement.getSQLXML( index ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||
return getObject( statement.getSQLXML( name ), options );
|
||||
}
|
||||
|
||||
private X getObject(SQLXML sqlxml, WrapperOptions options) throws SQLException {
|
||||
if ( sqlxml == null ) {
|
||||
return null;
|
||||
}
|
||||
return options.getSessionFactory().getFastSessionServices().getXmlFormatMapper().fromString(
|
||||
sqlxml.getString(),
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected static class XmlValueBinder<X> extends BasicBinder<X> {
|
||||
public XmlValueBinder(JavaType<X> javaType, JdbcType jdbcType) {
|
||||
super( javaType, jdbcType );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
final String xml = options.getSessionFactory().getFastSessionServices().getXmlFormatMapper().toString(
|
||||
value,
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
SQLXML sqlxml = st.getConnection().createSQLXML();
|
||||
sqlxml.setString( xml );
|
||||
st.setSQLXML( index, sqlxml );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
final String xml = options.getSessionFactory().getFastSessionServices().getXmlFormatMapper().toString(
|
||||
value,
|
||||
getJavaType(),
|
||||
options
|
||||
);
|
||||
SQLXML sqlxml = st.getConnection().createSQLXML();
|
||||
sqlxml.setString( xml );
|
||||
st.setSQLXML( name, sqlxml );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ import org.hibernate.persister.entity.EntityPersister;
|
|||
import org.hibernate.property.access.spi.GetterFieldImpl;
|
||||
import org.hibernate.property.access.spi.GetterMethodImpl;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.type.descriptor.java.spi.JdbcTypeRecommendationException;
|
||||
|
||||
import org.hibernate.testing.ServiceRegistryBuilder;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
@ -58,7 +59,7 @@ public class AccessMappingTest {
|
|||
sf = cfg.buildSessionFactory( serviceRegistry );
|
||||
fail( "@Id and @OneToMany are not placed consistently in test entities. SessionFactory creation should fail." );
|
||||
}
|
||||
catch (MappingException e) {
|
||||
catch (MappingException | JdbcTypeRecommendationException e) {
|
||||
// success
|
||||
}
|
||||
finally {
|
||||
|
|
|
@ -19,6 +19,9 @@ import java.util.ArrayList;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.SAXReader;
|
||||
|
@ -31,10 +34,16 @@ public class TestDataReader {
|
|||
}
|
||||
List<TestDataElement> testDataElements = new ArrayList<TestDataElement>();
|
||||
SAXReader reader = new SAXReader();
|
||||
// Make sure we use the "right" implementation as the Oracle XDB driver comes with a JAXP implementation as well
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance( "org.apache.xerces.jaxp.SAXParserFactoryImpl", null );
|
||||
factory.setValidating(false);
|
||||
factory.setNamespaceAware(true);
|
||||
try {
|
||||
reader.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false );
|
||||
reader.setFeature( "http://xml.org/sax/features/external-general-entities", false );
|
||||
reader.setFeature( "http://xml.org/sax/features/external-parameter-entities", false );
|
||||
factory.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false );
|
||||
factory.setFeature( "http://xml.org/sax/features/external-general-entities", false );
|
||||
factory.setFeature( "http://xml.org/sax/features/external-parameter-entities", false );
|
||||
SAXParser parser = factory.newSAXParser();
|
||||
reader.setXMLReader( parser.getXMLReader() );
|
||||
Document document = reader.read( getInputStream( fileName ) );
|
||||
addDataElements( document, testDataElements );
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ dependencies {
|
|||
javadocClasspath jakartaLibs.validation
|
||||
javadocClasspath jakartaLibs.cdi
|
||||
javadocClasspath jakartaLibs.jacc
|
||||
javadocClasspath jakartaLibs.jsonb
|
||||
javadocClasspath jakartaLibs.jsonbApi
|
||||
javadocClasspath libs.ant
|
||||
javadocClasspath dbLibs.postgresql
|
||||
javadocClasspath libs.jackson
|
||||
|
|
|
@ -83,6 +83,7 @@ dependencyResolutionManagement {
|
|||
alias( "classmate" ).to( "com.fasterxml", "classmate" ).version( "1.5.1" )
|
||||
|
||||
alias( "jackson" ).to ( "com.fasterxml.jackson.core", "jackson-databind" ).version( "2.13.0" )
|
||||
alias( "jacksonXml" ).to ( "com.fasterxml.jackson.dataformat", "jackson-dataformat-xml" ).version( "2.13.0" )
|
||||
alias( "validator" ).to( "org.hibernate.validator", "hibernate-validator" ).version( "7.0.4.Final" )
|
||||
|
||||
alias( "ant" ).to( "org.apache.ant", "ant" ).version( "1.8.2" )
|
||||
|
@ -103,6 +104,7 @@ dependencyResolutionManagement {
|
|||
}
|
||||
jakartaLibs {
|
||||
version( "jaxbRuntime", "3.0.2" )
|
||||
version( "jsonbRuntime", "2.0.4" )
|
||||
|
||||
// `jakartaJpaVersion` comes from the `` plugin - accounting for command-line overriding of the JPA version to use
|
||||
alias( "jpa" ).to( "jakarta.persistence", "jakarta.persistence-api" ).version( "${jakartaJpaVersion}" )
|
||||
|
@ -110,7 +112,8 @@ dependencyResolutionManagement {
|
|||
alias( "validation" ).to( "jakarta.validation", "jakarta.validation-api" ).version( "3.0.0" )
|
||||
alias( "jacc" ).to( "jakarta.authorization", "jakarta.authorization-api" ).version( "2.0.0" )
|
||||
alias( "cdi" ).to( "jakarta.enterprise", "jakarta.enterprise.cdi-api" ).version( "3.0.0" )
|
||||
alias( "jsonb" ).to( "jakarta.json.bind", "jakarta.json.bind-api" ).version( "2.0.0" )
|
||||
alias( "jsonbApi" ).to( "jakarta.json.bind", "jakarta.json.bind-api" ).version( "2.0.0" )
|
||||
alias( "jsonb" ).to( "org.eclipse", "yasson" ).versionRef( "jsonbRuntime" )
|
||||
alias( "inject" ).to( "jakarta.inject", "jakarta.inject-api" ).version( "2.0.0" )
|
||||
alias( "jaxbApi" ).to( "jakarta.xml.bind", "jakarta.xml.bind-api" ).version( "3.0.1" )
|
||||
alias( "jaxb" ).to( "org.glassfish.jaxb", "jaxb-runtime" ).versionRef( "jaxbRuntime" )
|
||||
|
@ -160,6 +163,7 @@ dependencyResolutionManagement {
|
|||
version( "h2", "2.1.210" )
|
||||
version( "pgsql", "42.2.16" )
|
||||
version( "mysql", "8.0.27" )
|
||||
version( "oracle", "21.3.0.0" )
|
||||
|
||||
alias( "h2" ).to( "com.h2database", "h2" ).versionRef( "h2" )
|
||||
alias( "h2gis" ).to( "org.orbisgis", "h2gis" ).version( "2.0.0" )
|
||||
|
@ -170,7 +174,9 @@ dependencyResolutionManagement {
|
|||
alias( "mysql" ).to( "mysql", "mysql-connector-java" ).versionRef( "mysql" )
|
||||
alias( "tidb" ).to( "mysql", "mysql-connector-java" ).versionRef( "mysql" )
|
||||
alias( "mariadb" ).to( "org.mariadb.jdbc", "mariadb-java-client" ).version( "2.2.3" )
|
||||
alias( "oracle" ).to( "com.oracle.database.jdbc", "ojdbc8" ).version( "21.3.0.0" )
|
||||
alias( "oracle" ).to( "com.oracle.database.jdbc", "ojdbc8" ).versionRef( "oracle" )
|
||||
alias( "oracleXml" ).to( "com.oracle.database.xml", "xdb" ).versionRef( "oracle" )
|
||||
alias( "oracleXmlParser" ).to( "com.oracle.database.xml", "xmlparserv2" ).versionRef( "oracle" )
|
||||
alias( "mssql" ).to( "com.microsoft.sqlserver", "mssql-jdbc" ).version( "7.2.1.jre8" )
|
||||
alias( "db2" ).to( "com.ibm.db2", "jcc" ).version( "11.5.7.0" )
|
||||
alias( "hana" ).to( "com.sap.cloud.db.jdbc", "ngdbc" ).version( "2.4.59" )
|
||||
|
|
Loading…
Reference in New Issue