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
|
@ -5579,7 +5579,29 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1007,4 +1007,9 @@ public class H2Dialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsBindingNullSqlTypeForSetNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1513,4 +1513,9 @@ public class MySQLDialect extends Dialect {
|
|||
? sqlCheckConstraint + " " + checkConstraint.getOptions()
|
||||
: sqlCheckConstraint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsBindingNullSqlTypeForSetNull() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1498,4 +1498,9 @@ public class PostgreSQLDialect extends Dialect {
|
|||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsBindingNullSqlTypeForSetNull() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1136,4 +1136,9 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
private String getCheckConstraintOptions(CheckConstraint checkConstraint) {
|
||||
return isNotEmpty( checkConstraint.getOptions() ) ? checkConstraint.getOptions() + " " : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsBindingNullForSetObject() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,13 +42,27 @@ public class ObjectNullResolvingJdbcType extends ObjectJdbcType {
|
|||
@Override
|
||||
protected void doBindNull(PreparedStatement st, int index, WrapperOptions options)
|
||||
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
|
||||
protected void doBindNull(CallableStatement st, String name, WrapperOptions options)
|
||||
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
|
||||
|
|
|
@ -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