Skip to content

BAH-4717|Shilpa|Forcing execution of SqlSearchService read only - #323

Open
shilpa-iplit wants to merge 2 commits into
masterfrom
BAH-4717
Open

BAH-4717|Shilpa|Forcing execution of SqlSearchService read only#323
shilpa-iplit wants to merge 2 commits into
masterfrom
BAH-4717

Conversation

@shilpa-iplit

@shilpa-iplit shilpa-iplit commented May 18, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • Improvements
    • Search operations now run with safer read-only database handling, improving reliability and resource cleanup.
  • Tests
    • Test suite consolidated path handling for test resources to make tests more robust and consistent.
  • Chores
    • Test resource configuration updated to treat large media files separately for more reliable test builds.

Review Change Stack

@shilpa-iplit
shilpa-iplit requested review from angshu and mohan-13 May 18, 2026 06:06
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7fbd10f8-c3ba-4d1d-9f54-d681e58ad90e

📥 Commits

Reviewing files that changed from the base of the PR and between 3f6ec2f and 3c23bd2.

📒 Files selected for processing (2)
  • bahmni-emr-api/src/test/java/org/openmrs/module/bahmniemrapi/encountertransaction/advice/BahmniEncounterTransactionUpdateAdviceTest.java
  • bahmnicore-api/pom.xml

📝 Walkthrough

Walkthrough

Adds @Transactional(readOnly = true) to the SqlSearchService.search interface method, refactors SqlSearchServiceImpl.search to nested try-with-resources with an explicit read-only Connection, centralizes test resource path resolution in a test helper, and splits POM testResources handling for media files.

Changes

Read-only Transaction Semantics for SQL Search

Layer / File(s) Summary
Transaction annotation and JDBC resource management
bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/SqlSearchService.java, bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java
SqlSearchService.search is annotated with @Transactional(readOnly = true). SqlSearchServiceImpl.search() refactors JDBC handling to nested try-with-resources and sets the connection to read-only while preserving result mapping.

Test helper consolidation

Layer / File(s) Summary
Test resources helper and test updates
bahmni-emr-api/src/test/java/org/openmrs/module/bahmniemrapi/encountertransaction/advice/BahmniEncounterTransactionUpdateAdviceTest.java
Adds imports for File and URISyntaxException, introduces getTestResourcesDirectory() helper, and updates three tests to use the helper when stubbing OpenmrsUtil.getApplicationDataDirectory().

POM testResources split

Layer / File(s) Summary
POM testResources handling
bahmnicore-api/pom.xml
Splits testResources into a filtered entry that excludes **/*.mkv and **/*.mov and an unfiltered entry that includes only those media patterns.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 In read-only woods I hop and peek,
Connections close precise and meek,
Resources try-with-love, no mess,
Tests find paths with tidy finesse,
A rabbit nods: the search is sleek. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding @Transactional(readOnly=true) to SqlSearchService.search() to enforce read-only transaction semantics for SQL search operations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch BAH-4717

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java (1)

47-48: 💤 Low value

Consider simplifying to a single try-with-resources statement.

The nested try-with-resources blocks are functionally correct but more verbose than necessary. Java's try-with-resources supports multiple resources in a single declaration, which is more concise and equally safe.

♻️ Simplified try-with-resources structure
         try (Connection conn = DatabaseUpdater.getConnection()) {
             conn.setReadOnly(true);
-            try (PreparedStatement statement = sqlQueryHelper.constructPreparedStatement(query, mergedParams, conn);
-                ResultSet resultSet = statement.executeQuery()) {
+            try (PreparedStatement statement = sqlQueryHelper.constructPreparedStatement(query, mergedParams, conn);
+                 ResultSet resultSet = statement.executeQuery()) {
                 RowMapper rowMapper = new RowMapper();
                 while (resultSet.next()) {
                     results.add(rowMapper.mapRow(resultSet));

Note: The original single-level try with semicolon-separated resources is also valid and even more concise, unless the explicit nesting was intentional for readability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java`
around lines 47 - 48, In SqlSearchServiceImpl.java, simplify the nested
try-with-resources around PreparedStatement and ResultSet into a single try
declaration: obtain the PreparedStatement from
sqlQueryHelper.constructPreparedStatement(query, mergedParams, conn) and in the
same try list the ResultSet from statement.executeQuery(); replace the inner
nested try block with a single try (PreparedStatement statement = ...; ResultSet
resultSet = statement.executeQuery()) { ... } so both resources are closed
automatically; keep the existing variables (query, mergedParams, conn) and
existing result processing logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java`:
- Around line 45-54: The code in SqlSearchServiceImpl uses a raw JDBC Connection
from DatabaseUpdater.getConnection() and then calls conn.setReadOnly(true),
which means the `@Transactional`(readOnly = true) on the interface method is
ineffective; either remove the `@Transactional` annotation from the service
interface/method to avoid misleading semantics, or refactor SqlSearchServiceImpl
to obtain connections via the Spring-managed DataSource (inject DataSource or
use JdbcTemplate/NamedParameterJdbcTemplate) so Spring transaction management
and `@Transactional`(readOnly = true) actually control the connection; locate
DatabaseUpdater.getConnection(), the conn.setReadOnly(true) call, and the
service method signature (with `@Transactional`) to implement one of these two
fixes consistently across the service.

In
`@bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/SqlSearchService.java`:
- Line 13: The `@Transactional` on SqlSearchService is misleading because
DatabaseUpdater.getConnection() uses DriverManager.getConnection() and bypasses
Spring transactions; remove the `@Transactional`(readOnly = true) annotation from
the method or class and either (a) refactor the code that uses
DatabaseUpdater.getConnection() to obtain connections from Spring-managed
DataSource (e.g., inject DataSource and call dataSource.getConnection()) so
operations participate in transactions, or (b) explicitly manage transactions
around the DriverManager connection; update SqlSearchService and any methods
that call DatabaseUpdater.getConnection() accordingly and add
documentation/comments indicating the chosen approach.

---

Nitpick comments:
In
`@bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java`:
- Around line 47-48: In SqlSearchServiceImpl.java, simplify the nested
try-with-resources around PreparedStatement and ResultSet into a single try
declaration: obtain the PreparedStatement from
sqlQueryHelper.constructPreparedStatement(query, mergedParams, conn) and in the
same try list the ResultSet from statement.executeQuery(); replace the inner
nested try block with a single try (PreparedStatement statement = ...; ResultSet
resultSet = statement.executeQuery()) { ... } so both resources are closed
automatically; keep the existing variables (query, mergedParams, conn) and
existing result processing logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0b3b0cc7-8426-44e6-8737-37764275856b

📥 Commits

Reviewing files that changed from the base of the PR and between d12197d and 3f6ec2f.

📒 Files selected for processing (2)
  • bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/SqlSearchService.java
  • bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java

Comment on lines +45 to 54
try (Connection conn = DatabaseUpdater.getConnection()) {
conn.setReadOnly(true);
try (PreparedStatement statement = sqlQueryHelper.constructPreparedStatement(query, mergedParams, conn);
ResultSet resultSet = statement.executeQuery()) {
RowMapper rowMapper = new RowMapper();
while (resultSet.next()) {
results.add(rowMapper.mapRow(resultSet));
}
return results;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how other services in the codebase handle transactions with JDBC
rg -n -B5 -A10 'DatabaseUpdater\.getConnection' --type=java

# Look for DataSource usage with `@Transactional` in the codebase
rg -nP -B2 -A8 '`@Transactional`.*\n.*DataSource' --type=java

# Check if there's a pattern of using Spring-managed connections elsewhere
rg -n -C5 'JdbcTemplate|DataSource' --type=java -g '*Service*.java'

Repository: Bahmni/bahmni-core

Length of output: 2500


🏁 Script executed:

# Find the DatabaseUpdater class to understand getConnection()
fd -n "DatabaseUpdater.java" --type f

Repository: Bahmni/bahmni-core

Length of output: 293


🏁 Script executed:

# Find the interface and method definition for the search method
rg -n "interface SqlSearchService|search\(" bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java -B 5 -A 15

Repository: Bahmni/bahmni-core

Length of output: 1319


🏁 Script executed:

# Look for `@Transactional` annotation on this specific method
rg -n "`@Transactional`" bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java -B 2 -A 5

Repository: Bahmni/bahmni-core

Length of output: 44


🏁 Script executed:

# Find DatabaseUpdater class
find . -name "DatabaseUpdater.java" -type f

Repository: Bahmni/bahmni-core

Length of output: 44


🏁 Script executed:

# Find SqlSearchService interface definition
find . -name "SqlSearchService.java" -type f

Repository: Bahmni/bahmni-core

Length of output: 152


🏁 Script executed:

# Look for class-level `@Transactional` on SqlSearchServiceImpl
rg -B 10 "public class SqlSearchServiceImpl" bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java

Repository: Bahmni/bahmni-core

Length of output: 358


🏁 Script executed:

# Read the SqlSearchService interface to check for `@Transactional`
cat bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/SqlSearchService.java

Repository: Bahmni/bahmni-core

Length of output: 498


🏁 Script executed:

# Check if DatabaseUpdater is defined in this repo or if it's from OpenMRS
find . -name "DatabaseUpdater.java" -type f

Repository: Bahmni/bahmni-core

Length of output: 44


🏁 Script executed:

# Search for other methods that use DatabaseUpdater.getConnection() to see if this is a pattern
rg -n "DatabaseUpdater" bahmnicore-api/src/main/java --type=java

Repository: Bahmni/bahmni-core

Length of output: 369


🏁 Script executed:

# Check if there are other `@Transactional` methods in the codebase and how they handle connections
rg -n "`@Transactional`" bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/ --type=java -A 3 | head -50

Repository: Bahmni/bahmni-core

Length of output: 6251


Address the mismatch between Spring transaction management and actual JDBC connection handling.

The @Transactional(readOnly = true) annotation on the interface method cannot manage the connection obtained via DatabaseUpdater.getConnection(). Since DatabaseUpdater.getConnection() returns a raw JDBC connection from OpenMRS (external utility), Spring's transaction manager has no control over it. The explicit conn.setReadOnly(true) at line 46 is the only enforcement mechanism that actually works, but this creates confusion about transaction semantics:

  • Developers reading this code see @Transactional and assume Spring is enforcing read-only semantics
  • The annotation has no actual effect on the database connection
  • This pattern is inconsistent with the rest of the codebase, where @Transactional properly manages connections through DAOs

Either remove the @Transactional annotation (since the connection is manually managed), or refactor to obtain the connection through Spring's DataSource so the annotation becomes meaningful.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/impl/SqlSearchServiceImpl.java`
around lines 45 - 54, The code in SqlSearchServiceImpl uses a raw JDBC
Connection from DatabaseUpdater.getConnection() and then calls
conn.setReadOnly(true), which means the `@Transactional`(readOnly = true) on the
interface method is ineffective; either remove the `@Transactional` annotation
from the service interface/method to avoid misleading semantics, or refactor
SqlSearchServiceImpl to obtain connections via the Spring-managed DataSource
(inject DataSource or use JdbcTemplate/NamedParameterJdbcTemplate) so Spring
transaction management and `@Transactional`(readOnly = true) actually control the
connection; locate DatabaseUpdater.getConnection(), the conn.setReadOnly(true)
call, and the service method signature (with `@Transactional`) to implement one of
these two fixes consistently across the service.


public interface SqlSearchService {

@Transactional(readOnly = true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for DataSourceTransactionManager or transaction manager configuration
rg -n -C5 --type=xml 'transactionManager|DataSourceTransactionManager' 

# Check DatabaseUpdater.getConnection() implementation
rg -n -C10 'class DatabaseUpdater' --type=java

# Look for how DatabaseUpdater is used with transactions in the codebase
rg -n -C5 'DatabaseUpdater\.getConnection\(\)' --type=java

# Check if there are other services using `@Transactional` with DatabaseUpdater
rg -nP -C3 '`@Transactional`.*DatabaseUpdater' --type=java

Repository: Bahmni/bahmni-core

Length of output: 15362


🌐 Web query:

Does OpenMRS DatabaseUpdater.getConnection() participate in Spring transaction management?

💡 Result:

No, the OpenMRS DatabaseUpdater.getConnection method does not participate in Spring transaction management [1][2]. The method is designed to provide a raw database connection specifically for Liquibase to perform database schema updates [1][3][2]. Its implementation retrieves connection details directly from the OpenMRS runtime properties and establishes a new connection using DriverManager.getConnection [1]: public static Connection getConnection throws Exception { Properties props = Context.getRuntimeProperties; //... retrieves driver, url, username, password from properties... Class.forName(driver); return DriverManager.getConnection(url, username, password); } Because it uses DriverManager directly rather than a Spring-managed DataSource or a transaction-aware utility (such as Spring's DataSourceUtils), any connection obtained through this method operates outside the scope of Spring's transaction synchronization [4]. Consequently, it will not be aware of, nor participate in, any active Spring-managed transactions [4][5].

Citations:


The @Transactional annotation is ineffective with DatabaseUpdater.getConnection().

OpenMRS's DatabaseUpdater.getConnection() uses DriverManager.getConnection() directly and does not participate in Spring's transaction management. Any SQL executed on a connection obtained this way operates outside Spring's transaction scope and will not be aware of or participate in any active Spring-managed transactions. This makes the annotation misleading—it creates the false impression that the method is transaction-managed when the actual database operations are not.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/service/SqlSearchService.java`
at line 13, The `@Transactional` on SqlSearchService is misleading because
DatabaseUpdater.getConnection() uses DriverManager.getConnection() and bypasses
Spring transactions; remove the `@Transactional`(readOnly = true) annotation from
the method or class and either (a) refactor the code that uses
DatabaseUpdater.getConnection() to obtain connections from Spring-managed
DataSource (e.g., inject DataSource and call dataSource.getConnection()) so
operations participate in transactions, or (b) explicitly manage transactions
around the DriverManager connection; update SqlSearchService and any methods
that call DatabaseUpdater.getConnection() accordingly and add
documentation/comments indicating the chosen approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant