Skip to content

Commit 3c4a340

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 2e54d95 commit 3c4a340

File tree

7 files changed

+160
-4
lines changed

7 files changed

+160
-4
lines changed

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5577,9 +5577,31 @@ public String appendCheckConstraintOptions(CheckConstraint checkConstraint, Stri
55775577
/**
55785578
* Does this dialect support appending table options SQL fragment at the end of the SQL Table creation statement?
55795579
*
5580-
* @return {@code true} indicates it does; {@code false} indicates it does not;
5580+
* @return {@code true} indicates it does; {@code false} indicates it does not;
55815581
*/
5582-
public boolean supportsTableOptions(){
5582+
public boolean supportsTableOptions() {
5583+
return false;
5584+
}
5585+
5586+
/**
5587+
* Does this dialect support binding {@link Types#NULL} for {@link PreparedStatement#setNull(int, int)}?
5588+
* if it does, then call of {@link PreparedStatement#getParameterMetaData()} could be eliminated for better performance.
5589+
*
5590+
* @return {@code true} indicates it does; {@code false} indicates it does not;
5591+
* @see org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType
5592+
*/
5593+
public boolean supportsBindingNullSqlTypeForSetNull() {
5594+
return false;
5595+
}
5596+
5597+
/**
5598+
* Does this dialect support binding {@code null} for {@link PreparedStatement#setObject(int, Object)}?
5599+
* if it does, then call of {@link PreparedStatement#getParameterMetaData()} could be eliminated for better performance.
5600+
*
5601+
* @return {@code true} indicates it does; {@code false} indicates it does not;
5602+
* @see org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType
5603+
*/
5604+
public boolean supportsBindingNullForSetObject() {
55835605
return false;
55845606
}
55855607

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,4 +1007,9 @@ public boolean supportsCaseInsensitiveLike(){
10071007
return true;
10081008
}
10091009

1010+
@Override
1011+
public boolean supportsBindingNullSqlTypeForSetNull() {
1012+
return true;
1013+
}
1014+
10101015
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,4 +1513,9 @@ public String appendCheckConstraintOptions(CheckConstraint checkConstraint, Stri
15131513
? sqlCheckConstraint + " " + checkConstraint.getOptions()
15141514
: sqlCheckConstraint;
15151515
}
1516+
1517+
@Override
1518+
public boolean supportsBindingNullSqlTypeForSetNull() {
1519+
return true;
1520+
}
15161521
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,4 +1498,9 @@ public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
14981498
public boolean supportsFromClauseInUpdate() {
14991499
return true;
15001500
}
1501+
1502+
@Override
1503+
public boolean supportsBindingNullSqlTypeForSetNull() {
1504+
return true;
1505+
}
15011506
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,4 +1136,9 @@ public String getCheckConstraintString(CheckConstraint checkConstraint) {
11361136
private String getCheckConstraintOptions(CheckConstraint checkConstraint) {
11371137
return isNotEmpty( checkConstraint.getOptions() ) ? checkConstraint.getOptions() + " " : "";
11381138
}
1139+
1140+
@Override
1141+
public boolean supportsBindingNullForSetObject() {
1142+
return true;
1143+
}
11391144
}

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)