HHH-16320 Handle json columns with native ddl type on H2
This commit is contained in:
parent
39f4fdda5e
commit
c3fa3ae777
|
@ -75,6 +75,7 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2
|
|||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||
|
@ -94,6 +95,7 @@ import static org.hibernate.type.SqlTypes.DOUBLE;
|
|||
import static org.hibernate.type.SqlTypes.FLOAT;
|
||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||
import static org.hibernate.type.SqlTypes.INTERVAL_SECOND;
|
||||
import static org.hibernate.type.SqlTypes.JSON;
|
||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||
|
@ -248,6 +250,9 @@ public class H2LegacyDialect extends Dialect {
|
|||
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INTERVAL_SECOND, "interval second($p,$s)", this ) );
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,6 +270,9 @@ public class H2LegacyDialect extends Dialect {
|
|||
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2FormatJsonJdbcType.INSTANCE );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -392,6 +400,9 @@ public class H2LegacyDialect extends Dialect {
|
|||
if ( "GEOMETRY".equals( columnTypeName ) ) {
|
||||
return jdbcTypeRegistry.getDescriptor( GEOMETRY );
|
||||
}
|
||||
else if ( "JSON".equals( columnTypeName ) ) {
|
||||
return jdbcTypeRegistry.getDescriptor( JSON );
|
||||
}
|
||||
break;
|
||||
}
|
||||
return super.resolveSqlTypeDescriptor( columnTypeName, jdbcTypeCode, precision, scale, jdbcTypeRegistry );
|
||||
|
|
|
@ -68,6 +68,7 @@ import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation;
|
|||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||
|
@ -88,6 +89,7 @@ import static org.hibernate.type.SqlTypes.DOUBLE;
|
|||
import static org.hibernate.type.SqlTypes.FLOAT;
|
||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||
import static org.hibernate.type.SqlTypes.INTERVAL_SECOND;
|
||||
import static org.hibernate.type.SqlTypes.JSON;
|
||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||
|
@ -229,6 +231,9 @@ public class H2Dialect extends Dialect {
|
|||
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INTERVAL_SECOND, "interval second($p,$s)", this ) );
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -243,6 +248,9 @@ public class H2Dialect extends Dialect {
|
|||
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2FormatJsonJdbcType.INSTANCE );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -370,6 +378,9 @@ public class H2Dialect extends Dialect {
|
|||
if ( "GEOMETRY".equals( columnTypeName ) ) {
|
||||
return jdbcTypeRegistry.getDescriptor( GEOMETRY );
|
||||
}
|
||||
else if ( "JSON".equals( columnTypeName ) ) {
|
||||
return jdbcTypeRegistry.getDescriptor( JSON );
|
||||
}
|
||||
break;
|
||||
}
|
||||
return super.resolveSqlTypeDescriptor( columnTypeName, jdbcTypeCode, precision, scale, jdbcTypeRegistry );
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
|
||||
/**
|
||||
* Specialized type mapping for {@code JSON} that utilizes the custom
|
||||
* '{@code ? format json}' write expression for H2.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
public class H2FormatJsonJdbcType extends JsonJdbcType {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final H2FormatJsonJdbcType INSTANCE = new H2FormatJsonJdbcType( null );
|
||||
|
||||
protected H2FormatJsonJdbcType(EmbeddableMappingType embeddableMappingType) {
|
||||
super( embeddableMappingType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FormatJsonJdbcType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregateJdbcType resolveAggregateJdbcType(
|
||||
EmbeddableMappingType mappingType,
|
||||
String sqlType,
|
||||
RuntimeModelCreationContext creationContext) {
|
||||
return new H2FormatJsonJdbcType( mappingType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendWriteExpression(String writeExpression, SqlAppender appender, Dialect dialect) {
|
||||
appender.append( writeExpression );
|
||||
appender.append( " format json" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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.orm.test.type;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.Jira;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialect;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SessionFactory
|
||||
@DomainModel( annotatedClasses = { H2JsonListTest.Path.class, H2JsonListTest.PathClob.class } )
|
||||
@RequiresDialect( H2Dialect.class )
|
||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-16320" )
|
||||
public class H2JsonListTest {
|
||||
@BeforeAll
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
session.persist( new Path( List.of( UUID.randomUUID(), UUID.randomUUID() ) ) );
|
||||
session.persist( new PathClob( List.of( UUID.randomUUID(), UUID.randomUUID() ) ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public void tearDown(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
session.createMutationQuery( "delete from Path" ).executeUpdate();
|
||||
session.createMutationQuery( "delete from PathClob" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrievalJson(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Path path = session.find( Path.class, 1L );
|
||||
assertThat( path ).isNotNull();
|
||||
assertThat( path.getRelativePaths() ).hasSize( 2 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeSyntaxJson(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
session.createNativeMutationQuery( "insert into paths (relativePaths,id) values (?1 FORMAT JSON, ?2)" )
|
||||
.setParameter(
|
||||
1,
|
||||
"[\"2b099c92-95ff-42e0-9f8c-f08c2518792d\", \"8d2164db-86b4-460a-91d0-bf821a8ca3d7\"]"
|
||||
)
|
||||
.setParameter( 2, 99L )
|
||||
.executeUpdate();
|
||||
} );
|
||||
scope.inTransaction( session -> {
|
||||
final Path path = session.createNativeQuery(
|
||||
"select * from paths_clob where id = 99",
|
||||
Path.class
|
||||
).getSingleResult();
|
||||
assertThat( path ).isNotNull();
|
||||
assertThat( path.getRelativePaths() ).hasSize( 2 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrievalClob(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final PathClob path = session.find( PathClob.class, 1L );
|
||||
assertThat( path ).isNotNull();
|
||||
assertThat( path.getRelativePaths() ).hasSize( 2 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeSyntaxClob(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
session.createNativeMutationQuery( "insert into paths_clob (relativePaths,id) values (?1 FORMAT JSON, ?2)" )
|
||||
.setParameter(
|
||||
1,
|
||||
"[\"2b099c92-95ff-42e0-9f8c-f08c2518792d\", \"8d2164db-86b4-460a-91d0-bf821a8ca3d7\"]"
|
||||
)
|
||||
.setParameter( 2, 99L )
|
||||
.executeUpdate();
|
||||
} );
|
||||
scope.inTransaction( session -> {
|
||||
final PathClob path = session.createNativeQuery(
|
||||
"select * from paths_clob where id = 99",
|
||||
PathClob.class
|
||||
).getSingleResult();
|
||||
assertThat( path ).isNotNull();
|
||||
assertThat( path.getRelativePaths() ).hasSize( 2 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity( name = "Path" )
|
||||
@Table( name = "paths" )
|
||||
public static class Path {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long id;
|
||||
|
||||
@JdbcTypeCode( SqlTypes.JSON )
|
||||
public List<UUID> relativePaths;
|
||||
|
||||
public Path() {
|
||||
}
|
||||
|
||||
public Path(List<UUID> relativePaths) {
|
||||
this.relativePaths = relativePaths;
|
||||
}
|
||||
|
||||
public List<UUID> getRelativePaths() {
|
||||
return relativePaths;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "PathClob" )
|
||||
@Table( name = "paths_clob" )
|
||||
public static class PathClob {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long id;
|
||||
|
||||
@JdbcTypeCode( SqlTypes.JSON )
|
||||
@Column( columnDefinition = "clob" )
|
||||
public List<UUID> relativePaths;
|
||||
|
||||
public PathClob() {
|
||||
}
|
||||
|
||||
public PathClob(List<UUID> relativePaths) {
|
||||
this.relativePaths = relativePaths;
|
||||
}
|
||||
|
||||
public List<UUID> getRelativePaths() {
|
||||
return relativePaths;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue