Skip to content

Commit de029ad

Browse files
alismanclaude
andcommitted
Optimize PatientMapper IN clauses for ClickHouse JDBC performance (#11673)
Replace foreach loops generating multiple prepared statement parameters with single array parameter using ArrayTypeHandler. This significantly improves performance with ClickHouse JDBC connections by reducing parameter overhead. - Use CONCAT(study_id, ':', patient_id) with ArrayTypeHandler - Add SqlUtils.combineStudyAndPatientIds() utility method - Apply optimization to both patient and sample lookup queries - Maintain security through proper parameter binding 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude <[email protected]>
1 parent 611aa91 commit de029ad

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.cbioportal.legacy.persistence.mybatis.util;
2+
3+
import java.util.List;
4+
5+
/** Utility class for SQL operations in MyBatis mappers */
6+
public class SqlUtils {
7+
8+
/**
9+
* Combines study IDs and patient/sample IDs into unique keys for efficient array parameter usage.
10+
* This helps optimize ClickHouse JDBC performance by reducing the number of prepared statement
11+
* parameters.
12+
*
13+
* @param studyIds List of study identifiers
14+
* @param entityIds List of patient or sample identifiers (corresponding to studyIds by index)
15+
* @return Array of combined unique keys in format "studyId:entityId"
16+
*/
17+
public static String[] combineStudyAndPatientIds(List<String> studyIds, List<String> entityIds) {
18+
if (studyIds == null || entityIds == null || studyIds.size() != entityIds.size()) {
19+
throw new IllegalArgumentException(
20+
"studyIds and entityIds must be non-null and have the same size");
21+
}
22+
23+
String[] combinedKeys = new String[studyIds.size()];
24+
for (int i = 0; i < studyIds.size(); i++) {
25+
combinedKeys[i] = studyIds.get(i) + ":" + entityIds.get(i);
26+
}
27+
28+
return combinedKeys;
29+
}
30+
}

src/main/resources/org/cbioportal/legacy/persistence/mybatis/PatientMapper.xml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@
3838
cancer_study.cancer_study_identifier = #{studyIds[0]}
3939
</if>
4040
<if test="patientIds != null">
41-
(cancer_study.cancer_study_identifier,patient.stable_id) IN
42-
<foreach index="i" collection="patientIds" open="(" separator="," close=")">(#{studyIds[${i}]},#{patientIds[${i}]})</foreach>
41+
<bind name="patientUniqueKeys" value="@org.cbioportal.legacy.persistence.mybatis.util.SqlUtils@combineStudyAndPatientIds(studyIds, patientIds)" />
42+
CONCAT(cancer_study.cancer_study_identifier, ':', patient.stable_id) IN (
43+
#{patientUniqueKeys, typeHandler=org.apache.ibatis.type.ArrayTypeHandler}
44+
)
4345
</if>
4446
</if>
4547
<if test="keyword != null">
@@ -110,8 +112,10 @@
110112
<foreach item="item" collection="sampleIds" open="(" separator="," close=")">#{item}</foreach>
111113
</if>
112114
<if test="@java.util.Arrays@stream(studyIds.toArray()).distinct().count() > 1">
113-
(cancer_study.cancer_study_identifier,sample.stable_id) IN
114-
<foreach index="i" collection="sampleIds" open="(" separator="," close=")">(#{studyIds[${i}]},#{sampleIds[${i}]})</foreach>
115+
<bind name="sampleUniqueKeys" value="@org.cbioportal.legacy.persistence.mybatis.util.SqlUtils@combineStudyAndPatientIds(studyIds, sampleIds)" />
116+
CONCAT(cancer_study.cancer_study_identifier, ':', sample.stable_id) IN (
117+
#{sampleUniqueKeys, typeHandler=org.apache.ibatis.type.ArrayTypeHandler}
118+
)
115119
</if>
116120
<if test="@java.util.Arrays@stream(studyIds.toArray()).distinct().count() == 0">
117121
FALSE

0 commit comments

Comments
 (0)