Skip to content

Commit a0bf95e

Browse files
quaffbeikov
authored andcommitted
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()`.
1 parent de25ece commit a0bf95e

File tree

7 files changed

+158
-2
lines changed

7 files changed

+158
-2
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5774,4 +5774,26 @@ public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSuppor
57745774
return FunctionalDependencyAnalysisSupportImpl.NONE;
57755775
}
57765776

5777+
/**
5778+
* Does this dialect support binding {@link Types#NULL} for {@link PreparedStatement#setNull(int, int)}?
5779+
* if it does, then call of {@link PreparedStatement#getParameterMetaData()} could be eliminated for better performance.
5780+
*
5781+
* @return {@code true} indicates it does; {@code false} indicates it does not;
5782+
* @see org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType
5783+
*/
5784+
public boolean supportsBindingNullSqlTypeForSetNull() {
5785+
return false;
5786+
}
5787+
5788+
/**
5789+
* Does this dialect support binding {@code null} for {@link PreparedStatement#setObject(int, Object)}?
5790+
* if it does, then call of {@link PreparedStatement#getParameterMetaData()} could be eliminated for better performance.
5791+
*
5792+
* @return {@code true} indicates it does; {@code false} indicates it does not;
5793+
* @see org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType
5794+
*/
5795+
public boolean supportsBindingNullForSetObject() {
5796+
return false;
5797+
}
5798+
57775799
}

hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,4 +1028,9 @@ public String getDual() {
10281028
return "dual";
10291029
}
10301030

1031+
@Override
1032+
public boolean supportsBindingNullSqlTypeForSetNull() {
1033+
return true;
1034+
}
1035+
10311036
}

hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,4 +1577,9 @@ public boolean supportsFromClauseInUpdate() {
15771577
public String getDual() {
15781578
return "dual";
15791579
}
1580+
1581+
@Override
1582+
public boolean supportsBindingNullSqlTypeForSetNull() {
1583+
return true;
1584+
}
15801585
}

hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,4 +1609,9 @@ public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
16091609
public boolean supportsFromClauseInUpdate() {
16101610
return true;
16111611
}
1612+
1613+
@Override
1614+
public boolean supportsBindingNullSqlTypeForSetNull() {
1615+
return true;
1616+
}
16121617
}

hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,4 +1202,9 @@ public CallableStatementSupport getCallableStatementSupport() {
12021202
return SQLServerCallableStatementSupport.INSTANCE;
12031203
}
12041204

1205+
1206+
@Override
1207+
public boolean supportsBindingNullForSetObject() {
1208+
return true;
1209+
}
12051210
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ObjectNullResolvingJdbcType.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,27 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
4242
@Override
4343
protected void doBindNull(PreparedStatement st, int index, WrapperOptions options)
4444
throws SQLException {
45-
st.setNull( index, st.getParameterMetaData().getParameterType( index ) );
45+
if ( options.getDialect().supportsBindingNullForSetObject() ) {
46+
st.setObject( index, null );
47+
}
48+
else {
49+
final int sqlType = options.getDialect().supportsBindingNullSqlTypeForSetNull() ? Types.NULL
50+
: st.getParameterMetaData().getParameterType( index );
51+
st.setNull( index, sqlType );
52+
}
4653
}
4754

4855
@Override
4956
protected void doBindNull(CallableStatement st, String name, WrapperOptions options)
5057
throws SQLException {
51-
st.setNull( name, Types.JAVA_OBJECT );
58+
if ( options.getDialect().supportsBindingNullForSetObject() ) {
59+
st.setObject( name, null );
60+
}
61+
else {
62+
final int sqlType = options.getDialect().supportsBindingNullSqlTypeForSetNull() ? Types.NULL
63+
: Types.JAVA_OBJECT;
64+
st.setNull( name, sqlType );
65+
}
5266
}
5367

5468
@Override
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.orm.test.typedescriptor;
8+
9+
import org.hibernate.testing.orm.junit.DomainModel;
10+
import org.hibernate.testing.orm.junit.JiraKey;
11+
import org.hibernate.testing.orm.junit.SessionFactory;
12+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
13+
import org.junit.jupiter.api.AfterEach;
14+
import org.junit.jupiter.api.BeforeEach;
15+
import org.junit.jupiter.api.Test;
16+
17+
import jakarta.persistence.Entity;
18+
import jakarta.persistence.GeneratedValue;
19+
import jakarta.persistence.Id;
20+
21+
import static org.junit.jupiter.api.Assertions.assertNotNull;
22+
23+
/**
24+
* @author Yanming Zhou
25+
*/
26+
@DomainModel(
27+
annotatedClasses = NullTest.SimpleEntity.class
28+
)
29+
@SessionFactory
30+
public class NullTest {
31+
32+
@BeforeEach
33+
public void setUp(SessionFactoryScope scope) {
34+
scope.inTransaction(
35+
session -> session.persist( new SimpleEntity() )
36+
);
37+
}
38+
39+
@AfterEach
40+
public void tearDown(SessionFactoryScope scope) {
41+
scope.inTransaction(
42+
session ->
43+
session.createMutationQuery( "delete from SimpleEntity" ).executeUpdate()
44+
);
45+
}
46+
47+
@Test
48+
@JiraKey("HHH-18581")
49+
public void passingNullAsParameterOfNativeQuery(SessionFactoryScope scope) {
50+
scope.inTransaction(
51+
session -> {
52+
SimpleEntity persisted = session.createNativeQuery(
53+
"select * from SimpleEntity where name is null or name=:name",
54+
SimpleEntity.class
55+
).setParameter( "name", null ).uniqueResult();
56+
57+
assertNotNull( persisted );
58+
}
59+
);
60+
}
61+
62+
@Test
63+
public void passingNullAsParameterOfQuery(SessionFactoryScope scope) {
64+
scope.inTransaction(
65+
session -> {
66+
SimpleEntity persisted = session.createQuery(
67+
"from SimpleEntity where name is null or name=:name",
68+
SimpleEntity.class
69+
).setParameter( "name", null ).uniqueResult();
70+
71+
assertNotNull( persisted );
72+
}
73+
);
74+
}
75+
76+
@Entity(name = "SimpleEntity")
77+
static class SimpleEntity {
78+
@Id
79+
@GeneratedValue
80+
private Integer id;
81+
82+
private String name;
83+
84+
public Integer getId() {
85+
return id;
86+
}
87+
88+
public void setId(Integer id) {
89+
this.id = id;
90+
}
91+
92+
public String getName() {
93+
return name;
94+
}
95+
96+
public void setName(String name) {
97+
this.name = name;
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)