diff --git a/documentation/documentation.gradle b/documentation/documentation.gradle index 90a26d3d29..d58e895ca0 100644 --- a/documentation/documentation.gradle +++ b/documentation/documentation.gradle @@ -1,7 +1,7 @@ import org.asciidoctor.gradle.jvm.AsciidoctorTask plugins { - id 'org.asciidoctor.jvm.convert' version '3.1.0' + id 'org.asciidoctor.jvm.convert' version '3.3.2' } /* diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/spatial/Spatial.adoc b/documentation/src/main/asciidoc/userguide/chapters/query/spatial/Spatial.adoc index 441bdb783b..5bd003060d 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/spatial/Spatial.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/query/spatial/Spatial.adoc @@ -12,10 +12,10 @@ and it allows you to deal with geographic data in a standardized way. Hibernate Spatial provides a standardized, cross-database interface to geographic data storage and query functions. It supports most of the functions described by the OGC Simple Feature Specification. Supported databases are Oracle 10g/11g, -PostgreSQL/PostGIS, MySQL, Microsoft SQL Server and H2/GeoDB. +PostgreSQL/PostGIS, MySQL, Microsoft SQL Server, DB2, CockroachDB and H2/GeoDB. Spatial data types are not part of the Java standard library, and they are absent from the JDBC specification. -Over the years https://tsusiatsoftware.net/jts/main.html[JTS] has emerged the _de facto_ standard to fill this gap. JTS is +Over the years https://tsusiatsoftware.net/jts/main.html[JTS] has emerged as the _de facto_ standard to fill this gap. JTS is an implementation of the https://portal.opengeospatial.org/files/?artifact_id=829[Simple Feature Specification (SFS)]. Many databases on the other hand implement the SQL/MM - Part 3: Spatial Data specification - a related, but broader specification. The biggest difference is that SFS is limited to 2D geometries in the projected plane (although JTS supports 3D coordinates), whereas @@ -26,7 +26,7 @@ https://github.com/GeoLatte/geolatte-geom[geolatte-geom]. As already mentioned, standard. Geolatte-geom (also written by the lead developer of Hibernate Spatial) is a more recent library that supports many features specified in SQL/MM but not available in JTS (such as support for 4D geometries, and support for extended WKT/WKB formats). Geolatte-geom also implements encoders/decoders for the database native types. Geolatte-geom has good interoperability with -JTS. Converting a Geolatte `geometry` to a JTS `geometry, for instance, doesn't require copying of the coordinates. +JTS. Converting a Geolatte `geometry` to a JTS `geometry`, for instance, doesn't require copying of the coordinates. It also delegates spatial processing to JTS. Whether you use JTS or Geolatte-geom, Hibernate spatial maps the database spatial types to your geometry model of choice. It will, however, @@ -260,7 +260,7 @@ create transform for db2gse.st_geometry db2_program ( Hibernate Spatial comes with the following types: jts_geometry:: - Handled by `org.hibernate.spatial.JTSGeometryType` it maps a database geometry column type to a `org.locationtech.jts.geom.Geometry` entity property type. + Handled by `org.hibernate.spatial.JTSGeometryType`, it maps a database geometry column type to a `org.locationtech.jts.geom.Geometry` entity property type. geolatte_geometry:: Handled by `org.hibernate.spatial.GeolatteGeometryType`, it maps a database geometry column type to an `org.geolatte.geom.Geometry` entity property type. diff --git a/hibernate-core/src/main/java/org/hibernate/LockMode.java b/hibernate-core/src/main/java/org/hibernate/LockMode.java index a0d2c63e4f..ce1c3f432e 100644 --- a/hibernate-core/src/main/java/org/hibernate/LockMode.java +++ b/hibernate-core/src/main/java/org/hibernate/LockMode.java @@ -147,6 +147,12 @@ public enum LockMode { return NONE; } + for ( LockMode lockMode : LockMode.values() ) { + if ( lockMode.externalForm.equals( externalForm ) ) { + return lockMode; + } + } + for ( LockMode lockMode : LockMode.values() ) { if ( lockMode.externalForm.equalsIgnoreCase( externalForm ) ) { return lockMode; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/StandardStack.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/StandardStack.java index 91a8ab5099..f4e4a449a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/StandardStack.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/StandardStack.java @@ -6,117 +6,117 @@ */ package org.hibernate.internal.util.collections; -import java.util.LinkedList; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; import java.util.function.Consumer; import java.util.function.Function; /** - * A general-purpose stack impl. + * A general-purpose stack impl supporting null values. * * @param The type of things stored in the stack * * @author Steve Ebersole + * @author Sanne Grinovero */ public final class StandardStack implements Stack { - @SuppressWarnings("unchecked") - private final T nullMarker = (T) new Object(); - private T current; - - private final LinkedList internalStack = new LinkedList<>(); + private ArrayDeque internalStack; + private static final Object NULL_TOKEN = new Object(); public StandardStack() { } public StandardStack(T initial) { - current = initial; + stackInstanceExpected().addFirst( initial ); } @Override public void push(T newCurrent) { + Object toStore = newCurrent; if ( newCurrent == null ) { - newCurrent = nullMarker; + toStore = NULL_TOKEN; } + stackInstanceExpected().addFirst( toStore ); + } - if ( current != null ) { - internalStack.addFirst( current ); + private Deque stackInstanceExpected() { + if ( internalStack == null ) { + //"7" picked to use 8, but skipping the odd initialCapacity method + internalStack = new ArrayDeque<>( 7 ); } - - current = newCurrent; + return internalStack; } @Override public T pop() { - final T popped = this.current; - if ( internalStack.isEmpty() ) { - this.current = null; - } - else { - this.current = internalStack.removeFirst(); - } + return convert( stackInstanceExpected().removeFirst() ); + } - return popped == nullMarker ? null : popped; + private T convert(final Object internalStoredObject) { + if ( internalStoredObject == NULL_TOKEN ) { + return null; + } + return (T) internalStoredObject; } @Override public T getCurrent() { - return current == nullMarker ? null : current; + if ( internalStack == null ) { + return null; + } + return convert( internalStack.peek() ); } @Override public int depth() { - if ( current == null ) { + if ( internalStack == null ) { return 0; } - else if ( internalStack.isEmpty() ) { - return 1; - } - else { - return internalStack.size() + 1; - } + return internalStack.size(); } @Override public boolean isEmpty() { - return current == null; + if ( internalStack == null ) { + return true; + } + return internalStack.isEmpty(); } @Override public void clear() { - current = null; - internalStack.clear(); + if ( internalStack != null ) { + internalStack.clear(); + } } @Override public void visitRootFirst(Consumer action) { - final int stackSize = internalStack.size(); - for ( int i = stackSize - 1; i >= 0; i-- ) { - action.accept( internalStack.get( i ) ); + if ( internalStack == null ) { + return; } - if ( current != null ) { - action.accept( current ); + final Iterator iterator = internalStack.descendingIterator(); + while ( iterator.hasNext() ) { + action.accept( iterator.next() ); } } @Override public X findCurrentFirst(Function function) { - if ( current != null ) { - { - final X result = function.apply( current ); - - if ( result != null ) { - return result; - } - } - - for ( T t : internalStack ) { - final X result = function.apply( t ); - if ( result != null ) { - return result; - } + if ( internalStack == null ) { + return null; + } + final Iterator iterator = internalStack.iterator(); + while ( iterator.hasNext() ) { + final X result = function.apply( iterator.next() ); + if ( result != null ) { + return result; } } return null; } + } diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/LockModeTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/LockModeTypeHelper.java index 4a34b696ec..d9a18005dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/LockModeTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/LockModeTypeHelper.java @@ -39,21 +39,10 @@ public final class LockModeTypeHelper { return getLockMode( (LockModeType) value ); } else if ( String.class.isInstance( value ) ) { - // first try LockMode name - LockMode lockMode = LockMode.valueOf( (String) value ); - if ( lockMode == null ) { - try { - lockMode = getLockMode( LockModeType.valueOf( (String) value ) ); - } - catch ( Exception ignore ) { - } - } - if ( lockMode != null ) { - return lockMode; - } + return LockMode.fromExternalForm( (String) value ); } - throw new IllegalArgumentException( "Unknown lock mode source : " + value ); + throw new IllegalArgumentException( "Unknown lock mode source: '" + value + "'; can't convert from value of type " + value.getClass() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index 921a07396f..9764261b23 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -49,6 +49,7 @@ import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.internal.AbstractSharedSessionContract; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.jpa.internal.util.LockModeTypeHelper; import org.hibernate.jpa.spi.NativeQueryTupleTransformer; import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.metamodel.model.domain.BasicDomainType; @@ -1065,6 +1066,9 @@ public class NativeQueryImpl else if ( value instanceof LockModeType ) { applyLockModeTypeHint( (LockModeType) value ); } + else if ( String.class.isInstance( value ) ) { + applyHibernateLockModeHint( LockModeTypeHelper.interpretLockMode( value ) ); + } else { throw new IllegalArgumentException( String.format( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryTest.java index e5125f711f..470480aaf7 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryTest.java @@ -16,8 +16,10 @@ import jakarta.persistence.NamedQueries; import jakarta.persistence.NamedQuery; import jakarta.persistence.Query; +import org.hibernate.LockMode; import org.hibernate.Session; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.jpa.QueryHints; import org.hibernate.query.NativeQuery; import org.hibernate.testing.TestForIssue; @@ -192,6 +194,18 @@ public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase { } ); } + @Test + @TestForIssue(jiraKey = "HHH-14816") + public void testQueryHintLockMode() { + doInJPA( this::entityManagerFactory, entityManager -> { + Query query = entityManager.createNamedQuery( "NamedNativeQuery" ); + query.setHint( QueryHints.HINT_NATIVE_LOCKMODE, "none" ); + query.setParameter( 1, GAME_TITLES[0] ); + assertEquals( LockMode.NONE, query.getHints().get( QueryHints.HINT_NATIVE_LOCKMODE ) ); + } + ); + } + @Entity(name = "Game") @NamedQueries(@NamedQuery(name = "NamedQuery", query = "select g from Game g where title = ?1")) @NamedNativeQueries(@NamedNativeQuery(name = "NamedNativeQuery", query = "select * from Game g where title = ?"))