
001package org.hl7.fhir.r5.testfactory.dataprovider; 002 003import java.io.IOException; 004import java.sql.Connection; 005import java.sql.ResultSet; 006import java.sql.ResultSetMetaData; 007import java.sql.SQLException; 008import java.sql.Statement; 009import java.util.ArrayList; 010import java.util.HashMap; 011import java.util.List; 012import java.util.Map; 013 014import org.hl7.fhir.exceptions.FHIRException; 015import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 016 017/** 018 * Concrete implementation of TableDataProvider that reads data from a SQL database table. 019 */ 020@MarkedToMoveToAdjunctPackage 021public class SQLDataProvider extends TableDataProvider { 022 023 private Connection connection; 024 private String tableName; 025 private List<String> columnHeaders; 026 private ResultSet resultSet; 027 private ResultSetMetaData metaData; 028 private Map<String, Integer> columnIndexMap = new HashMap<>(); 029 private int counter; 030 031 /** 032 * Constructs an SQLDataProvider. 033 * 034 * @param connection The SQL database connection. 035 * @param tableName The name of the table to read. 036 * @throws IOException If a database access error occurs. 037 */ 038 public SQLDataProvider(Connection connection, String tableName) throws IOException { 039 this.connection = connection; 040 this.tableName = tableName; 041 042 reset(); 043 } 044 045 /** 046 * Loads the column headers from the table's metadata. 047 * 048 * @throws IOException If a database access error occurs. 049 */ 050 private void loadColumnHeaders() throws IOException { 051 try (Statement statement = connection.createStatement(); 052 ResultSet rs = statement.executeQuery("SELECT * FROM " + tableName + " WHERE 1=0")) { 053 metaData = rs.getMetaData(); 054 columnHeaders = new ArrayList<>(); 055 columnHeaders.add("counter"); 056 for (int i = 1; i <= metaData.getColumnCount(); i++) { 057 String columnName = metaData.getColumnName(i); 058 columnHeaders.add(columnName); 059 columnIndexMap.put(columnName, i); 060 } 061 } catch (SQLException e) { 062 throw new FHIRException("Error loading column headers: " + e.getMessage(), e); 063 } 064 } 065 066 /** 067 * Prepares the ResultSet for iterating over the table's rows. 068 * 069 * @throws IOException If a database access error occurs. 070 */ 071 private void prepareResultSet() throws IOException { 072 try { 073 Statement statement = connection.createStatement(); 074 resultSet = statement.executeQuery("SELECT * FROM " + tableName); 075 } catch (SQLException e) { 076 throw new IOException("Error preparing result set: " + e.getMessage(), e); 077 } 078 } 079 080 @Override 081 public List<String> columns() { 082 return columnHeaders; 083 } 084 085 @Override 086 public boolean nextRow() throws FHIRException { 087 try { 088 counter++; 089 return resultSet.next(); 090 } catch (SQLException e) { 091 throw new FHIRException("Error moving to next row: " + e.getMessage(), e); 092 } 093 } 094 095 @Override 096 public List<String> cells() throws FHIRException { 097 try { 098 List<String> cellValues = new ArrayList<>(); 099 cellValues.add(""+counter); 100 for (int i = 1; i <= metaData.getColumnCount(); i++) { 101 cellValues.add(resultSet.getString(i)); 102 } 103 return cellValues; 104 } catch (SQLException e) { 105 throw new FHIRException("Error retrieving row cells: " + e.getMessage(), e); 106 } 107 } 108 109 @Override 110 public String cell(String name) throws FHIRException { 111 if ("counter".equals(name)) { 112 return ""+counter; 113 } else { 114 try { 115 Integer columnIndex = columnIndexMap.get(name); 116 if (columnIndex == null) { 117 return null; 118 } 119 return resultSet.getString(columnIndex); 120 } catch (SQLException e) { 121 throw new FHIRException("Error retrieving cell value: " + e.getMessage(), e); 122 } 123 } 124 } 125 126 /** 127 * Closes the ResultSet and releases the database resources. 128 * 129 * @throws IOException If a database access error occurs. 130 */ 131 public void close() throws IOException { 132 try { 133 if (resultSet != null) { 134 resultSet.close(); 135 } 136 if (connection != null) { 137 connection.close(); 138 } 139 } catch (SQLException e) { 140 throw new IOException("Error closing resources: " + e.getMessage(), e); 141 } 142 } 143 144 @Override 145 public void reset() throws FHIRException { 146 try { 147 loadColumnHeaders(); 148 prepareResultSet(); 149 } catch (Exception e) { 150 throw new FHIRException("Error closing resources: " + e.getMessage(), e); 151 } 152 } 153}