001package net.sf.jkniv.whinstone.jdbc; 002 003import java.sql.Connection; 004import java.sql.DatabaseMetaData; 005import java.sql.PreparedStatement; 006import java.sql.SQLException; 007import java.sql.Statement; 008 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012import net.sf.jkniv.asserts.Assertable; 013import net.sf.jkniv.asserts.AssertsFactory; 014import net.sf.jkniv.exception.HandleableException; 015import net.sf.jkniv.sqlegance.Insertable; 016import net.sf.jkniv.sqlegance.RepositoryException; 017import net.sf.jkniv.sqlegance.Sql; 018import net.sf.jkniv.sqlegance.dialect.SqlDialect; 019import net.sf.jkniv.sqlegance.dialect.SqlFeatureSupport; 020import net.sf.jkniv.whinstone.ConnectionAdapter; 021import net.sf.jkniv.whinstone.Queryable; 022import net.sf.jkniv.whinstone.ResultRow; 023import net.sf.jkniv.whinstone.commands.Command; 024import net.sf.jkniv.whinstone.jdbc.commands.AddAutoKeyJdbcCommand; 025import net.sf.jkniv.whinstone.jdbc.commands.AddSequenceKeyJdbcCommand; 026import net.sf.jkniv.whinstone.jdbc.commands.BulkJdbcCommand; 027import net.sf.jkniv.whinstone.jdbc.commands.DefaultJdbcCommand; 028import net.sf.jkniv.whinstone.jdbc.commands.DefaultJdbcQuery; 029import net.sf.jkniv.whinstone.jdbc.commands.JdbcAutoGeneratedKey; 030import net.sf.jkniv.whinstone.jdbc.commands.JdbcSequenceGeneratedKey; 031import net.sf.jkniv.whinstone.jdbc.statement.JdbcPreparedStatementAdapter; 032import net.sf.jkniv.whinstone.statement.AutoKey; 033import net.sf.jkniv.whinstone.statement.StatementAdapter; 034import net.sf.jkniv.whinstone.transaction.TransactionContext; 035import net.sf.jkniv.whinstone.transaction.TransactionSessions; 036import net.sf.jkniv.whinstone.types.RegisterType; 037 038public class JdbcConnectionAdapter implements ConnectionAdapter 039{ 040 private static final Logger SQLLOG = net.sf.jkniv.whinstone.jdbc.LoggerFactory.getLogger(); 041 private static final Logger LOG = LoggerFactory.getLogger(JdbcConnectionAdapter.class); 042 private static final Assertable NOT_NULL = AssertsFactory.getNotNull(); 043 private final Connection conn; 044 private final String contextName; 045 private final HandleableException handlerException; 046 047 public JdbcConnectionAdapter(String contextName, Connection conn, HandleableException handlerException) 048 { 049 NOT_NULL.verify(conn, contextName); 050 this.contextName = contextName; 051 this.conn = conn; 052 this.handlerException = handlerException; 053 } 054 055 @Override 056 public String getContextName() 057 { 058 return this.contextName; 059 } 060 061 @Override 062 public void commit() throws SQLException 063 { 064 // try 065 // { 066 this.conn.commit(); 067 // } 068 // catch (SQLException sqle) 069 // { 070 // handlerException.handle(sqle, "COMMIT"); 071 // } 072 } 073 074 @Override 075 public void rollback() throws SQLException 076 { 077 // try 078 // { 079 this.conn.rollback(); 080 // } 081 // catch (SQLException sqle) 082 // { 083 // handlerException.handle(sqle, "ROLLBACK"); 084 // } 085 } 086 087 @Override 088 public void close() //throws SQLException 089 { 090 try 091 { 092 TransactionContext ctx = TransactionSessions.get(contextName); 093 if (ctx == null || !ctx.isActive()) 094 this.conn.close(); 095 } 096 catch (SQLException sqle) 097 { 098 throw new RepositoryException("Cannot close connection [" + sqle.getMessage() + "]", sqle); 099 //LOG.warn("Erro to closing connection. Reason: " + sqle.getMessage()); 100 } 101 } 102 103 @Override 104 public boolean isClosed() throws SQLException 105 { 106 // boolean closed = false; 107 // try 108 // { 109 return conn.isClosed(); 110 // } 111 // catch (SQLException sqle) 112 // { 113 // LOG.warn("Erro to check if connection is closed. Reason: " + sqle.getMessage()); 114 // } 115 // return closed; 116 } 117 118 @Override 119 public boolean isAutoCommit() throws SQLException 120 { 121 // boolean isAuto = false; 122 // try 123 // { 124 return conn.getAutoCommit(); 125 // } 126 // catch (SQLException sqle) 127 // { 128 // LOG.warn("Erro to check if connection is auto-commit. Reason: " + sqle.getMessage()); 129 // } 130 // return isAuto; 131 } 132 133 @Override 134 public void autoCommitOn() throws SQLException 135 { 136 // boolean autoOnChanged = false; 137 // try 138 // { 139 conn.setAutoCommit(true); 140 // autoOnChanged = true; 141 // } 142 // catch (SQLException sqle) 143 // { 144 // LOG.error("Erro to change connection to auto-commit[true]. Reason: " + sqle.getMessage()); 145 // } 146 // return autoOnChanged; 147 } 148 149 @Override 150 public void autoCommitOff() throws SQLException 151 { 152 // boolean autoOnChanged = false; 153 // try 154 // { 155 conn.setAutoCommit(false); 156 // autoOnChanged = true; 157 // } 158 // catch (SQLException sqle) 159 // { 160 // LOG.error("Erro to change connection to auto-commit[false]. Reason: " + sqle.getMessage()); 161 // } 162 // return autoOnChanged; 163 } 164 165 //@Override 166 @SuppressWarnings({ "rawtypes", "unchecked" }) 167 private <T, R> StatementAdapter<T, R> newStatement(Queryable queryable) 168 { 169 PreparedStatement stmt = prepareStatement(queryable); 170 StatementAdapter<T, R> adapter = new JdbcPreparedStatementAdapter(stmt, queryable); 171 return adapter; 172 } 173 174 @SuppressWarnings({ "unchecked", "rawtypes" }) 175 private <T, R> StatementAdapter<T, R> newInsertStatement(Queryable queryable) 176 { 177 Insertable isql = queryable.getDynamicSql().asInsertable(); 178 PreparedStatement stmt = null; 179 AutoKey auto = null; 180 if (isql.isAutoGenerateKey() && isql.getAutoGeneratedKey().isAutoStrategy()) 181 { 182 String[] columns = isql.getAutoGeneratedKey().getColumnsAsArray(); 183 stmt = prepareStatement(queryable, columns); 184 auto = new JdbcAutoGeneratedKey(stmt, handlerException); 185 } 186 else 187 { 188 stmt = prepareStatement(queryable); 189 auto = new JdbcSequenceGeneratedKey(isql, conn, handlerException); 190 191 } 192 StatementAdapter<T, R> adapter = new JdbcPreparedStatementAdapter(stmt, queryable); 193 adapter.with(auto); 194 return adapter; 195 } 196 197 /* 198 * Creates a {@code PreparedStatement} object object capable of returning the auto-generated 199 * keys designated by the given array. 200 * 201 * @param conn Opened connection to database 202 * @param queryable query object with parameters 203 * @param columnNames an array of column names indicating the columns that should be returned from the inserted row or rows 204 * @return a new PreparedStatement object, containing the pre-compiled SQL statement. 205 * @throws net.sf.jkniv.sqlegance.RepositoryException wrapper SQLException 206 * @see java.sql.SQLException 207 * 208 private PreparedStatement prepareStatement(Connection conn, Queryable queryable, String[] columnNames) 209 { 210 PreparedStatement stmt = null; 211 Sql isql = queryable.getDynamicSql(); 212 String[] paramsNames = queryable.getParamsNames(); 213 String query = queryable.query(); 214 try 215 { 216 if (LOG.isTraceEnabled()) 217 LOG.trace("Preparing SQL statement type with [{}] column names", paramsNames.length); 218 219 SQLLOG.info(query); 220 if (columnNames.length > 0) 221 stmt = conn.prepareStatement(query, columnNames); 222 else 223 stmt = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); 224 225 if (isql.getTimeout() > 0) 226 stmt.setQueryTimeout(isql.getTimeout()); 227 } 228 catch (SQLException sqle) 229 { 230 throw new RepositoryException("Cannot prepare statement!", sqle); 231 } 232 return stmt; 233 } 234 */ 235 236// @Override 237// @SuppressWarnings({ "rawtypes", "unchecked" }) 238// public <T, R> StatementAdapter<T, R> newStatement(String sql, LanguageType languageType) 239// { 240// PreparedStatement stmt = null; 241// StatementAdapter<T, R> adapter = null; 242// try 243// { 244// stmt = conn.prepareStatement(sql); 245// adapter = new net.sf.jkniv.whinstone.jdbc.statement.JdbcPreparedStatementAdapter(stmt, null); 246// } 247// catch (SQLException sqle) 248// { 249// throw new RepositoryException("Cannot prepare statement to [" + sql + "]", sqle); 250// } 251// return adapter; 252// } 253 254 @Override 255 public Object getMetaData() 256 { 257 DatabaseMetaData metadata = null; 258 try 259 { 260 metadata = conn.getMetaData(); 261 } 262 catch (SQLException sqle) 263 { 264 LOG.error("Erro to read data base meta data. Reason: " + sqle.getMessage()); 265 } 266 267 return metadata; 268 } 269 270 @Override 271 public Object unwrap() 272 { 273 return conn; 274 } 275 276 /** 277 * Creates a PreparedStatement object that will generate ResultSet objects with the given type, concurrency, and holdability. 278 * The parameters values is setting 279 * @param conn Opened connection to database 280 * @return a new PreparedStatement object, containing the pre-compiled SQL statement. 281 * @throws net.sf.jkniv.sqlegance.RepositoryException wrapper SQLException 282 * @see java.sql.SQLException 283 */ 284 private PreparedStatement prepareStatement(Queryable queryable) 285 { 286 PreparedStatement stmt = null; 287 Sql isql = queryable.getDynamicSql(); 288 if (LOG.isTraceEnabled()) 289 LOG.trace("Preparing SQL statement [{}] type [{}], concurrency [{}], holdability [{}] with [{}] parameters", 290 queryable.getName(), isql.getResultSetType(), isql.getResultSetConcurrency(), isql.getResultSetHoldability(), 291 queryable.getParamsNames().length); 292 stmt = buildNewStatement(queryable); 293 /* 294 SqlDialect dialect = queryable.getDynamicSql().getSqlDialect(); 295 int rsType = isql.getResultSetType().getTypeScroll(); 296 int rsConcurrency = isql.getResultSetConcurrency().getConcurrencyMode(); 297 int rsHoldability = isql.getResultSetHoldability().getHoldability(); 298 299 try 300 { 301 if (queryable.getDynamicSql().isInsertable()) 302 { 303 Insertable insertTag = isql.asInsertable(); 304 if (insertTag.isAutoGenerateKey() && insertTag.getAutoGeneratedKey().isAutoStrategy()) 305 { 306 String[] columns = insertTag.getAutoGeneratedKey().getColumnsAsArray(); 307 stmt = conn.prepareStatement(queryable.query(), columns); 308 } 309 } 310 if (stmt == null) 311 { 312 if (dialect.supportsStmtHoldability()) 313 stmt = conn.prepareStatement(queryable.query(), rsType, rsConcurrency, rsHoldability); 314 else 315 { 316 // SQLServer doesn't support Holdability 317 stmt = conn.prepareStatement(queryable.query(), rsType, rsConcurrency); 318 if (dialect.supportsConnHoldability()) 319 conn.setHoldability(rsHoldability); 320 } 321 } 322 if (isql.getTimeout() > 0) 323 stmt.setQueryTimeout(isql.getTimeout()); 324 } 325 catch (SQLException sqle) 326 { 327 throw new RepositoryException("Cannot prepare statement [" + sqle.getMessage() + "]", sqle); 328 }*/ 329 return stmt; 330 } 331 332 private PreparedStatement buildNewStatement(Queryable queryable) 333 { 334 PreparedStatement stmt = null; 335 Sql isql = queryable.getDynamicSql(); 336 int rsType = isql.getResultSetType().getTypeScroll(); 337 int rsConcurrency = isql.getResultSetConcurrency().getConcurrencyMode(); 338 int rsHoldability = isql.getResultSetHoldability().getHoldability(); 339 SqlDialect sqlDialect = queryable.getDynamicSql().getSqlDialect(); 340 String query = queryable.query(); 341 try 342 { 343 SQLLOG.info(query); 344 if (isql.isInsertable()) 345 { 346 Insertable insertTag = isql.asInsertable(); 347 if (insertTag.isAutoGenerateKey() && insertTag.getAutoGeneratedKey().isAutoStrategy()) 348 { 349 String[] columns = insertTag.getAutoGeneratedKey().getColumnsAsArray(); 350 stmt = conn.prepareStatement(query, columns); 351 } 352 } 353 if (stmt == null) 354 { 355 if (sqlDialect.supportsFeature(SqlFeatureSupport.STMT_HOLDABILITY)) 356 stmt = conn.prepareStatement(query, rsType, rsConcurrency, rsHoldability); 357 else 358 { 359 // SQLServer/Oracle12 doesn't support Holdability 360 stmt = conn.prepareStatement(query, rsType, rsConcurrency); 361 if (sqlDialect.supportsFeature(SqlFeatureSupport.CONN_HOLDABILITY)) 362 conn.setHoldability(rsHoldability); 363 } 364 } 365 if (isql.getTimeout() > 0) 366 stmt.setQueryTimeout(isql.getTimeout()); 367 } 368 catch (SQLException sqle) 369 { 370 throw new RepositoryException("Cannot prepare statement [" + sqle.getMessage() + "]\n"+query, sqle); 371 } 372 return stmt; 373 } 374 375 /** 376 * Creates a {@code PreparedStatement} object object capable of returning the auto-generated 377 * keys designated by the given array. 378 * 379 * @param conn Opened connection to database 380 * @param columnNames an array of column names indicating the columns that should be returned from the inserted row or rows 381 * @return a new PreparedStatement object, containing the pre-compiled SQL statement. 382 * @throws net.sf.jkniv.sqlegance.RepositoryException wrapper SQLException 383 * @see java.sql.SQLException 384 */ 385 private PreparedStatement prepareStatement(Queryable queryable, String[] columnNames) 386 { 387 PreparedStatement stmt = null; 388 Sql isql = queryable.getDynamicSql(); 389 try 390 { 391 if (LOG.isTraceEnabled()) 392 LOG.trace("Preparing SQL statement type with [{}] column names", queryable.getParamsNames().length); 393 394 String query = queryable.query(); 395 SQLLOG.info(query); 396 if (columnNames.length > 0) 397 stmt = conn.prepareStatement(query, columnNames); 398 else 399 stmt = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); 400 401 if (isql.getTimeout() > 0) 402 stmt.setQueryTimeout(isql.getTimeout()); 403 } 404 catch (SQLException sqle) 405 { 406 throw new RepositoryException("Cannot prepare statement!", sqle); 407 } 408 return stmt; 409 } 410 411 @Override 412 public <T, R> Command asUpdateCommand(Queryable queryable) 413 { 414 Command command = null; 415 if (queryable.isTypeOfBulk()) 416 command = new BulkJdbcCommand(queryable, this.conn) 417 .with(this.newStatement(queryable)); 418 else 419 command = new DefaultJdbcCommand(queryable, this.conn) 420 .with(this.newStatement(queryable)); 421 422 return command; 423 } 424 425 @Override 426 public <T, R> Command asRemoveCommand(Queryable queryable) 427 { 428 Command command = null; 429 if (queryable.isTypeOfBulk()) 430 command = new BulkJdbcCommand(queryable, this.conn) 431 .with(this.newStatement(queryable)); 432 else 433 command = new DefaultJdbcCommand(queryable, this.conn) 434 .with(this.newStatement(queryable)); 435 436 return command; 437 } 438 439 @Override 440 public <T, R> Command asAddCommand(Queryable queryable) 441 { 442 Command command = null; 443 Insertable sql = queryable.getDynamicSql().asInsertable(); 444 if (sql.isAutoGenerateKey()) 445 { 446 if (sql.getAutoGeneratedKey().isAutoStrategy()) 447 { 448 StatementAdapter stmt = this.newInsertStatement(queryable); 449 command = new AddAutoKeyJdbcCommand(queryable, this.conn).with(stmt); 450 } 451 else if (sql.getAutoGeneratedKey().isSequenceStrategy()) 452 { 453 command = new AddSequenceKeyJdbcCommand(queryable, this.conn) 454 .with(this.newInsertStatement(queryable)); 455 } 456 } 457 else if (queryable.isTypeOfBulk()) 458 command = new BulkJdbcCommand(queryable, this.conn) 459 .with(this.newStatement(queryable)); 460 else 461 command = new DefaultJdbcCommand(queryable, this.conn) 462 .with(this.newStatement(queryable)); 463 464 return command; 465 } 466 467 @Override 468 public <T, R> Command asSelectCommand(Queryable queryable, ResultRow<T, R> overloadResultRow) 469 { 470 Command command = null; 471 //StatementAdapter<Number, ResultSet> adapterStmtCount = null; 472 //if (queryable.isPaging()) 473 // adapterStmtCount = newStatement(queryable.queryCount()); 474 475 StatementAdapter<T, R> stmt = this.newStatement(queryable); 476 //Selectable select = queryable.getDynamicSql().asSelectable(); 477 stmt.with(overloadResultRow); 478 //.oneToManies(select.getOneToMany()) 479 //.groupingBy(select.getGroupByAsList()) 480 481 command = new DefaultJdbcQuery(queryable, this.conn).with(stmt); 482 return command; 483 } 484 485}