HHH-18581 Introduce `supportsBindingNullSqlTypeForSetNull()` and `supportsBindingNullForSetObject()` for `Dialect` to optimize binding null
The method `PreparedStatement.getParameterMetaData().getParameterType(int)` call is expensive for some JDBC driver such as pgJDBC, we should avoid it if the driver supports binding `Types.NULL` for `setNull()` or `null` for `setObject()`.
This commit is contained in:
parent
2e54d95707
commit
3c4a340c5e
|
@ -5577,9 +5577,31 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
/**
|
/**
|
||||||
* Does this dialect support appending table options SQL fragment at the end of the SQL Table creation statement?
|
* Does this dialect support appending table options SQL fragment at the end of the SQL Table creation statement?
|
||||||
*
|
*
|
||||||
* @return {@code true} indicates it does; {@code false} indicates it does not;
|
* @return {@code true} indicates it does; {@code false} indicates it does not;
|
||||||
*/
|
*/
|
||||||
public boolean supportsTableOptions(){
|
public boolean supportsTableOptions() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this dialect support binding {@link Types#NULL} for {@link PreparedStatement#setNull(int, int)}?
|
||||||
|
* if it does, then call of {@link PreparedStatement#getParameterMetaData()} could be eliminated for better performance.
|
||||||
|
*
|
||||||
|
* @return {@code true} indicates it does; {@code false} indicates it does not;
|
||||||
|
* @see org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType
|
||||||
|
*/
|
||||||
|
public boolean supportsBindingNullSqlTypeForSetNull() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this dialect support binding {@code null} for {@link PreparedStatement#setObject(int, Object)}?
|
||||||
|
* if it does, then call of {@link PreparedStatement#getParameterMetaData()} could be eliminated for better performance.
|
||||||
|
*
|
||||||
|
* @return {@code true} indicates it does; {@code false} indicates it does not;
|
||||||
|
* @see org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType
|
||||||
|
*/
|
||||||
|
public boolean supportsBindingNullForSetObject() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1007,4 +1007,9 @@ public class H2Dialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsBindingNullSqlTypeForSetNull() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1513,4 +1513,9 @@ public class MySQLDialect extends Dialect {
|
||||||
? sqlCheckConstraint + " " + checkConstraint.getOptions()
|
? sqlCheckConstraint + " " + checkConstraint.getOptions()
|
||||||
: sqlCheckConstraint;
|
: sqlCheckConstraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsBindingNullSqlTypeForSetNull() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1498,4 +1498,9 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
public boolean supportsFromClauseInUpdate() {
|
public boolean supportsFromClauseInUpdate() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsBindingNullSqlTypeForSetNull() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1136,4 +1136,9 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
private String getCheckConstraintOptions(CheckConstraint checkConstraint) {
|
private String getCheckConstraintOptions(CheckConstraint checkConstraint) {
|
||||||
return isNotEmpty( checkConstraint.getOptions() ) ? checkConstraint.getOptions() + " " : "";
|
return isNotEmpty( checkConstraint.getOptions() ) ? checkConstraint.getOptions() + " " : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsBindingNullForSetObject() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,13 +42,27 @@ public class ObjectNullResolvingJdbcType extends ObjectJdbcType {
|
||||||
@Override
|
@Override
|
||||||
protected void doBindNull(PreparedStatement st, int index, WrapperOptions options)
|
protected void doBindNull(PreparedStatement st, int index, WrapperOptions options)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
st.setNull( index, st.getParameterMetaData().getParameterType( index ) );
|
if ( options.getDialect().supportsBindingNullForSetObject() ) {
|
||||||
|
st.setObject( index, null );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final int sqlType = options.getDialect().supportsBindingNullSqlTypeForSetNull() ? Types.NULL
|
||||||
|
: st.getParameterMetaData().getParameterType( index );
|
||||||
|
st.setNull( index, sqlType );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doBindNull(CallableStatement st, String name, WrapperOptions options)
|
protected void doBindNull(CallableStatement st, String name, WrapperOptions options)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
st.setNull( name, Types.JAVA_OBJECT );
|
if ( options.getDialect().supportsBindingNullForSetObject() ) {
|
||||||
|
st.setObject( name, null );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final int sqlType = options.getDialect().supportsBindingNullSqlTypeForSetNull() ? Types.NULL
|
||||||
|
: Types.JAVA_OBJECT;
|
||||||
|
st.setNull( name, sqlType );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* 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.typedescriptor;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Yanming Zhou
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = NullTest.SimpleEntity.class
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class NullTest {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.persist( new SimpleEntity() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session ->
|
||||||
|
session.createMutationQuery( "delete from SimpleEntity" ).executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-18581")
|
||||||
|
public void passingNullAsParameterOfNativeQuery(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
SimpleEntity persisted = session.createNativeQuery(
|
||||||
|
"select * from SimpleEntity where name is null or name=:name",
|
||||||
|
SimpleEntity.class
|
||||||
|
).setParameter( "name", null ).uniqueResult();
|
||||||
|
|
||||||
|
assertNotNull( persisted );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void passingNullAsParameterOfQuery(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
SimpleEntity persisted = session.createQuery(
|
||||||
|
"from SimpleEntity where name is null or name=:name",
|
||||||
|
SimpleEntity.class
|
||||||
|
).setParameter( "name", null ).uniqueResult();
|
||||||
|
|
||||||
|
assertNotNull( persisted );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "SimpleEntity")
|
||||||
|
static class SimpleEntity {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue