Skip to content

Commit c210908

Browse files
committed
HHH-18325: Prevent comparison between proxied and non-proxied statements
Some JDBC connection pools create a dynamic reflective Proxy wrapper around connections in the pool, plus the generated statements and the result sets those statements create, with the proxies allowing for methods calls to be intercepted by the connection pool for the purposes of returning items to the pool when they're released rather than destroying the underlying database connection. Tomcat's JDBC pool implementation does not correctly wrap the full chain of objects, meaning the raw Statement can be retrieved from a ResultSet rather than returning the proxied statement. This results in the ResourceRegistry cache attempting to store both the proxied Statement and the non-proxied Statement in its cache, but encountering an exception when the HashMap encounters two entries with the same HashCode and then attempts to differentiate them with an `equals` call which Tomcat's wrapper expects both instances to be proxied connections. To overcome this issue in Tomcat, as well as any other pool implementation which may use Proxy classes but leak the un-proxied entries, the original PreparedStatement used to create the ResultSet is being passed into the GeneratedValuesHelper for passing into the ResourceRegistry, rather than the GeneratedValues helper attempting to extract the Statement from the ResultSet.
1 parent a17b241 commit c210908

File tree

8 files changed

+2363
-20
lines changed

8 files changed

+2363
-20
lines changed

hibernate-core/hibernate-core.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ dependencies {
7070
testImplementation libs.jackson
7171
testRuntimeOnly libs.jacksonXml
7272
testRuntimeOnly libs.jacksonJsr310
73+
testImplementation testLibs.tomcatJdbc
7374

7475
testAnnotationProcessor project( ':hibernate-jpamodelgen' )
7576

hibernate-core/src/main/java/org/hibernate/generator/values/internal/GeneratedValuesHelper.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public class GeneratedValuesHelper {
6969
* Reads the {@link EntityPersister#getGeneratedProperties(EventType) generated values}
7070
* for the specified {@link ResultSet}.
7171
*
72+
* @param statement The prepared statement the result set was generated from
7273
* @param resultSet The result set from which to extract the generated values
7374
* @param persister The entity type which we're reading the generated values for
7475
* @param wrapperOptions The session
@@ -79,6 +80,7 @@ public class GeneratedValuesHelper {
7980
* @throws HibernateException Indicates a problem reading back a generated value
8081
*/
8182
public static GeneratedValues getGeneratedValues(
83+
PreparedStatement statement,
8284
ResultSet resultSet,
8385
EntityPersister persister,
8486
EventType timing,
@@ -100,6 +102,7 @@ public static GeneratedValues getGeneratedValues(
100102

101103
final GeneratedValuesImpl generatedValues = new GeneratedValuesImpl( generatedProperties );
102104
final Object[] results = readGeneratedValues(
105+
statement,
103106
resultSet,
104107
persister,
105108
mappingProducer,
@@ -125,6 +128,7 @@ public static GeneratedValues getGeneratedValues(
125128
* Utility method that reads the generated values from the specified {@link ResultSet}
126129
* using the {@link JdbcValuesMappingProducer} provided in input.
127130
*
131+
* @param statement the prepared statement that the result set was generated from
128132
* @param resultSet the result set containing the generated values
129133
* @param persister the current entity persister
130134
* @param mappingProducer the mapping producer to use when reading generated values
@@ -133,23 +137,18 @@ public static GeneratedValues getGeneratedValues(
133137
* @return an object array containing the generated values, order is consistent with the generated model parts list
134138
*/
135139
private static Object[] readGeneratedValues(
140+
PreparedStatement statement,
136141
ResultSet resultSet,
137142
EntityPersister persister,
138143
JdbcValuesMappingProducer mappingProducer,
139144
SharedSessionContractImplementor session) {
140145
final ExecutionContext executionContext = new BaseExecutionContext( session );
141146

142-
final DirectResultSetAccess directResultSetAccess;
143-
try {
144-
directResultSetAccess = new DirectResultSetAccess(
145-
session,
146-
(PreparedStatement) resultSet.getStatement(),
147-
resultSet
148-
);
149-
}
150-
catch (SQLException e) {
151-
throw new HibernateException( "Could not retrieve statement from generated values result set", e );
152-
}
147+
final DirectResultSetAccess directResultSetAccess = new DirectResultSetAccess(
148+
session,
149+
statement,
150+
resultSet
151+
);
153152

154153
final JdbcValues jdbcValues = new JdbcValuesResultSetImpl(
155154
directResultSetAccess,

hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,16 @@ protected void bindParameters(Object entity, PreparedStatement ps, SharedSession
6868
@Deprecated( forRemoval = true, since = "6.5" )
6969
protected Object extractGeneratedValues(ResultSet resultSet, SharedSessionContractImplementor session)
7070
throws SQLException {
71-
final GeneratedValues generatedValues = extractReturningValues( resultSet, session );
71+
final GeneratedValues generatedValues = extractReturningValues((PreparedStatement) resultSet.getStatement(), resultSet, session );
7272
return generatedValues.getGeneratedValue( persister.getIdentifierMapping() );
7373
}
7474

7575
/**
7676
* Extract the generated key value from the given result set after execution of {@link #getSelectSQL()}.
7777
*/
78-
private GeneratedValues extractReturningValues(ResultSet resultSet, SharedSessionContractImplementor session)
78+
private GeneratedValues extractReturningValues(PreparedStatement statement, ResultSet resultSet, SharedSessionContractImplementor session)
7979
throws SQLException {
80-
return getGeneratedValues( resultSet, persister, getTiming(), session );
80+
return getGeneratedValues( statement, resultSet, persister, getTiming(), session );
8181
}
8282

8383
@Override
@@ -118,7 +118,7 @@ public GeneratedValues performMutation(
118118

119119
final ResultSet resultSet = session.getJdbcCoordinator().getResultSetReturn().extract( idSelect, idSelectSql );
120120
try {
121-
return extractReturningValues( resultSet, session );
121+
return extractReturningValues( idSelect, resultSet, session );
122122
}
123123
catch (SQLException e) {
124124
throw jdbcServices.getSqlExceptionHelper().convert(
@@ -174,7 +174,7 @@ public final GeneratedValues performInsertReturning(String sql, SharedSessionCon
174174
bindParameters( binder.getEntity(), idSelect, session );
175175
ResultSet resultSet = jdbcCoordinator.getResultSetReturn().extract( idSelect, selectSQL );
176176
try {
177-
return extractReturningValues( resultSet, session );
177+
return extractReturningValues( idSelect, resultSet, session );
178178
}
179179
finally {
180180
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, idSelect );

hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public GeneratedValues performMutation(
118118
try {
119119
final ResultSet resultSet = preparedStatement.getGeneratedKeys();
120120
try {
121-
return getGeneratedValues( resultSet, persister, getTiming(), session );
121+
return getGeneratedValues( preparedStatement, resultSet, persister, getTiming(), session );
122122
}
123123
catch (SQLException e) {
124124
throw jdbcServices.getSqlExceptionHelper().convert(
@@ -170,7 +170,7 @@ public GeneratedValues executeAndExtractReturning(
170170
try {
171171
final ResultSet resultSet = preparedStatement.getGeneratedKeys();
172172
try {
173-
return getGeneratedValues( resultSet, persister, getTiming(), session );
173+
return getGeneratedValues( preparedStatement, resultSet, persister, getTiming(), session );
174174
}
175175
catch (SQLException e) {
176176
throw jdbcServices.getSqlExceptionHelper().convert(

hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ protected GeneratedValues executeAndExtractReturning(
9595

9696
final ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( preparedStatement, sql );
9797
try {
98-
return getGeneratedValues( resultSet, persister, getTiming(), session );
98+
return getGeneratedValues( preparedStatement, resultSet, persister, getTiming(), session );
9999
}
100100
catch (SQLException e) {
101101
throw jdbcServices.getSqlExceptionHelper().convert(

hibernate-core/src/main/java/org/hibernate/id/insert/SybaseJConnGetGeneratedKeysDelegate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public GeneratedValues executeAndExtractReturning(
6060

6161
ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( preparedStatement, sql );
6262
try {
63-
return getGeneratedValues( resultSet, persister, getTiming(), session );
63+
return getGeneratedValues( preparedStatement, resultSet, persister, getTiming(), session );
6464
}
6565
catch (SQLException e) {
6666
throw jdbcServices.getSqlExceptionHelper().convert(

0 commit comments

Comments
 (0)