Skip to content

Commit 8361b7f

Browse files
committed
HHH-19746 Test JPA Criteria parameter hashCode collision
1 parent 8dc6b85 commit 8361b7f

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.query.criteria;
6+
7+
import java.util.HashMap;
8+
9+
import org.hibernate.query.criteria.JpaParameterExpression;
10+
11+
import org.hibernate.testing.orm.junit.DomainModel;
12+
import org.hibernate.testing.orm.junit.Jira;
13+
import org.hibernate.testing.orm.junit.SessionFactory;
14+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
15+
import org.junit.jupiter.api.AfterEach;
16+
import org.junit.jupiter.api.Test;
17+
18+
import jakarta.persistence.Entity;
19+
import jakarta.persistence.Id;
20+
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertNotNull;
23+
24+
@DomainModel(annotatedClasses = CriteriaValueParameterTests.Cat.class)
25+
@SessionFactory
26+
@Jira("https://hibernate.atlassian.net/browse/HHH-19746")
27+
public class CriteriaValueParameterTests {
28+
29+
@AfterEach
30+
public void tearDown(SessionFactoryScope scope) {
31+
scope.getSessionFactory().getSchemaManager().truncateMappedObjects();
32+
}
33+
34+
@Test
35+
void test_named_parameter_with_same_hashcode_will_crash_the_query(SessionFactoryScope scope) {
36+
scope.inTransaction( session -> {
37+
var builder = session.getCriteriaBuilder();
38+
var criteria = builder.createQuery( Cat.class );
39+
var root = criteria.from( Cat.class );
40+
var p1 = builder.parameter( String.class, "Aa" );
41+
var p2 = builder.parameter( String.class, "BB" );
42+
assertEquals( p1.hashCode(), p2.hashCode() );
43+
criteria.where( root.get( "name" ).in( p1, p2 ) );
44+
var query = session.createQuery( criteria );
45+
query.setParameter( p1, "value" );
46+
query.setParameter( p2, "value" );
47+
query.getResultList();
48+
} );
49+
}
50+
51+
@Test
52+
void test_collisions_will_cause_bindings_to_get_lost(SessionFactoryScope scope) {
53+
scope.inTransaction( session -> {
54+
session.persist( new Cat( "a" ) );
55+
session.persist( new Cat( "b" ) );
56+
var builder = session.getCriteriaBuilder();
57+
var criteria = builder.createQuery( Long.class );
58+
var root = criteria.from( Cat.class );
59+
// Generating around 100K parameters so far always resulted in a hashCode collision
60+
var map = new HashMap<Integer, JpaParameterExpression<String>>( 100_000 );
61+
JpaParameterExpression<String> parameter1 = null;
62+
JpaParameterExpression<String> parameter2 = null;
63+
for ( int i = 0; i < 1_000_000; i++ ) {
64+
final JpaParameterExpression<String> parameter = builder.parameter( String.class );
65+
final JpaParameterExpression<String> old = map.put( parameter.hashCode(), parameter );
66+
if ( old != null ) {
67+
parameter1 = old;
68+
parameter2 = parameter;
69+
map.clear();
70+
break;
71+
}
72+
}
73+
assertNotNull( parameter1, "Failed to provoke hashCode collision" );
74+
criteria.select( builder.count( root ) )
75+
.where( root.get( "name" ).in( parameter1, parameter2 ) );
76+
var query = session.createQuery( criteria );
77+
query.setParameter( parameter1, "a" );
78+
query.setParameter( parameter2, "b" );
79+
assertEquals( 2, query.getSingleResult() );
80+
} );
81+
}
82+
83+
@Entity(name = "Cat")
84+
static class Cat {
85+
@Id
86+
private String name;
87+
88+
public Cat() {
89+
}
90+
91+
public Cat(String name) {
92+
this.name = name;
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)