import com.mysql.cj.jdbc.util.BaseBugReport; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.sql.ResultSet; import java.sql.DriverManager; import java.sql.Types; import java.util.Properties; import java.util.ArrayList; import java.util.List; public class CallableStatementBugReport extends BaseBugReport { private static final String DB_URL = "jdbc:mysql://localhost:3306/tmp?useSSL=false"; private static final String DB_USERNAME = "user"; private static final String DB_PASSWORD = "password"; private static final String ROUTINE_NAME = "get_setting"; private static final String TABLE_NAME = "TMP_SYSTEM_SETTINGS"; private static final String CHARSET = "utf8mb4"; private static final String COLLATE = "utf8mb4_unicode_ci"; private Connection connection = null; public Connection getCon() throws Exception { Properties props = new Properties(); props.setProperty("user", DB_USERNAME); props.setProperty("password", DB_PASSWORD); Connection connection = getConnection(DB_URL, props); return connection; } @Override public void setUp() throws Exception { String sql = ""; try (Statement statement = getCon().createStatement()) { statement.execute("DROP TABLE IF EXISTS " + TABLE_NAME); sql = "CREATE TABLE " + TABLE_NAME + "(" + " system_setting_uid int NOT NULL AUTO_INCREMENT," + " setting varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL," + " value varchar(2000) COLLATE utf8mb4_unicode_ci NOT NULL," + " created_date datetime NOT NULL DEFAULT CURRENT_TIMESTAMP," + " updated_date datetime DEFAULT NULL," + " PRIMARY KEY (system_setting_uid)," + " UNIQUE KEY setting (setting)" + ") ENGINE=InnoDB DEFAULT CHARSET=" + CHARSET + " COLLATE=" + COLLATE; statement.execute(sql); sql = "INSERT INTO " + TABLE_NAME + "(setting, value) VALUES('SETTING_1', '100'),('SETTING_2', '200'),('SETTING_3', '300')"; statement.execute(sql); statement.execute("DROP FUNCTION IF EXISTS " + ROUTINE_NAME); sql = "CREATE FUNCTION " + ROUTINE_NAME + "(p_setting VARCHAR(32)) RETURNS VARCHAR(2000) CHARSET " + CHARSET + " COLLATE " + COLLATE + " " + "BEGIN"+ " DECLARE v_value VARCHAR(2000);" + " DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_value = 'N/A';" + " SELECT value INTO v_value FROM " + TABLE_NAME + " WHERE setting = p_setting;" + " RETURN v_value;" + "END"; statement.execute(sql); statement.execute("DROP PROCEDURE IF EXISTS " + ROUTINE_NAME); sql = "CREATE PROCEDURE " + ROUTINE_NAME + "(IN p_setting VARCHAR(200), OUT message_out VARCHAR(200)) " + "BEGIN" + " SELECT setting, value, message_out FROM " + TABLE_NAME + " WHERE setting = p_setting;" + "END"; statement.execute(sql); } } @Override public void tearDown() throws Exception { try (Statement statement = getCon().createStatement()) { statement.execute("DROP FUNCTION IF EXISTS " + ROUTINE_NAME); statement.execute("DROP PROCEDURE IF EXISTS " + ROUTINE_NAME); statement.execute("DROP TABLE IF EXISTS " + TABLE_NAME); getCon().close(); } } @Override public void runTest() throws Exception { CallableStatement cs = null; try { List values = new ArrayList() { private static final long serialVersionUID = 1L; { add("SETTING_1"); add("SETTING_2"); add("SETTING_3"); add("NON_EXISTENT_SETTING"); }}; for(String val : values) { cs = getCon().prepareCall("{call " + ROUTINE_NAME + "(?, ?)}"); cs.setString(1, val); cs.registerOutParameter(2, Types.VARCHAR); cs.execute(); String message = cs.getString(2); if(message != null) { System.out.printf("out parameter value: %s\n", message); } else { ResultSet rs = cs.getResultSet(); boolean rowsReturned = ((rs != null) && rs.next()); if(rowsReturned) { String setting = rs.getString("setting"); String value = rs.getString("value"); System.out.printf("setting: %s, value: %s\n", setting, value); } else { System.out.printf("setting: %s does not exist.\n", val); } } } } catch(Exception e) { e.printStackTrace(); } finally { try { cs.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) throws Exception { new CallableStatementBugReport().run(); } }