1 22 23 28 29 package org.xquark.mapper.dbms; 30 31 import java.io.StringWriter ; 32 import java.sql.BatchUpdateException ; 33 import java.sql.PreparedStatement ; 34 import java.sql.SQLException ; 35 import java.util.*; 36 37 import org.apache.commons.logging.Log; 38 import org.apache.commons.logging.LogFactory; 39 import org.xquark.mapper.RepositoryException; 40 import org.xquark.mapper.util.Tabulator; 41 42 47 public class JDBCBatcher 48 { 49 private static final String RCSRevision = "$Revision: 1.3 $"; 50 private static final String RCSName = "$Name: $"; 51 52 private static Log log = LogFactory.getLog("internal." + JDBCBatcher.class.getName()); 53 54 final static int DEFAULT_BATCH_SIZE = 20; 55 56 57 HashMap statements; 58 59 60 List statementLists; 61 62 AbstractConnection conn; 63 64 protected boolean autoFlush = true; 65 66 67 public JDBCBatcher(AbstractConnection connection, boolean autoFlush) 68 { 69 statements = new HashMap(); 70 statementLists = new ArrayList(); 71 conn = connection; 72 this.autoFlush = autoFlush; 73 } 74 75 79 public int addStatement(PreparedStatement stmt, String tableName) 80 { 81 return addStatement(stmt, tableName, DEFAULT_BATCH_SIZE); 82 } 83 84 87 public int addStatement(PreparedStatement stmt, String tableName, int batchSize) 88 { 89 90 return addStatement(- 1, stmt, tableName, batchSize); 91 } 92 93 96 public int addStatement(int listIndex, PreparedStatement stmt, String tableName, int batchSize) 97 { 98 if (stmt == null) 99 return -1; 100 if (listIndex == -1) 101 { 102 listIndex = statementLists.size(); 103 statementLists.add(new ArrayList()); 104 } 105 StatementInfo info = new StatementInfo(batchSize, 106 listIndex, 107 ((List)statementLists.get(listIndex)).size(), 108 stmt, tableName); 109 statements.put(stmt, info); 110 ((List)statementLists.get(listIndex)).add(info); 111 return listIndex; 112 } 113 114 115 public boolean addBatch(PreparedStatement stmt, int line, int column) throws RepositoryException 116 { 117 StatementInfo info = (StatementInfo)statements.get(stmt); 118 try 119 { 120 if (info.batchSize != 1) conn.addBatch(stmt); 122 } 123 catch (SQLException e) 124 { 125 throw new RepositoryException( 126 RepositoryException.DB_ERROR, 127 "JDBC error while inserting tuple in batch.", 128 e); 129 } 130 if (info.inc(line, column)) { 132 if (log.isDebugEnabled()) 133 log.debug("JDBC batcher automatic flush triggered by\n" + info + '\n' + this); 134 flush(info.getListIndex(), info.getIndex()); 135 return true; 136 } 137 else 138 { 139 if (log.isDebugEnabled()) 140 log.debug("Add row to batch\n" + info); 141 return false; 142 } 143 } 144 145 146 public boolean addBatch(PreparedStatement stmt) throws RepositoryException 147 { 148 return addBatch(stmt, -1, -1); 149 } 150 151 public void setAutoFlush(boolean mode) 152 { 153 autoFlush = mode; 154 } 155 156 public void flush() throws RepositoryException 157 { 158 if (log.isDebugEnabled()) 159 log.debug("General JDBC batcher Forced Flush\n" + this); 160 Iterator it = statementLists.iterator(); 161 while (it.hasNext()) 162 { 163 flush((List)it.next()); 164 } 165 } 166 167 public void flush(int listIndex) throws RepositoryException 168 { 169 flush((List)statementLists.get(listIndex)); 170 } 171 172 private void flush(List stmtList) throws RepositoryException 173 { 174 for(int i = 0; i < stmtList.size(); i++) 175 { 176 flush((StatementInfo)stmtList.get(i)); 177 } 178 } 179 180 public String toString() 181 { 182 StringWriter sw = new StringWriter (); 183 Tabulator tab = new Tabulator(sw, 79, new int[] {0, 15, 30, 45, 60}); 184 tab.addItem("Table"); 185 tab.addItem("Batch size"); 186 tab.addItem("Batch count"); 187 tab.addItem("List index"); 188 tab.addItem("Stmt index"); 189 190 Iterator it = statementLists.iterator(); 191 List list; 192 StatementInfo si; 193 194 while (it.hasNext()) 195 { 196 list = (List)it.next(); 197 for(int i = 0; i < list.size(); i++) 198 { 199 si = (StatementInfo)list.get(i); 200 tab.addItem(si.tableName); 201 tab.addItem(si.batchSize); 202 tab.addItem(si.batchCount); 203 tab.addItem(si.listIndex); 204 tab.addItem(si.indexStatement); 205 } 206 tab.newLine(); 207 } 208 return sw.toString(); 209 } 210 211 public void flush(int indexList, int index) throws RepositoryException 212 { 213 flush((List)statementLists.get(indexList), index); 214 } 215 216 private void flush(List stmtList, int index) throws RepositoryException 217 { 218 for(int i = 0; i <= index; i++) 219 { 220 flush((StatementInfo)stmtList.get(i)); 221 } 222 } 223 224 public void flush(PreparedStatement stmt) throws RepositoryException 225 { 226 StatementInfo info = (StatementInfo)statements.get(stmt); 227 flush(info.getListIndex(), info.getIndex()); 228 } 229 230 231 private void flush(StatementInfo info) throws RepositoryException 232 { 233 int [] result = null; 234 try 235 { 236 if (info.getCount() > 0) 237 { 238 if (info.batchSize == 1) 239 info.getStatement().execute(); else 241 result = conn.executeBatch(info.getStatement()); 242 } 243 } 244 catch (BatchUpdateException e) 245 { 246 result = e.getUpdateCounts(); 247 int errorIndex = -1; 248 for (errorIndex = 0; (errorIndex < result.length) && (result[errorIndex] != -3); errorIndex++); 249 250 String message = "JDBC error while executing batch on table " + info.getTableName(); 251 LocatorImpl location = null; 252 253 if (errorIndex >= 0) 254 location = info.getLocator(errorIndex); 255 256 if (location == null) 257 throw new RepositoryException(RepositoryException.DB_ERROR, message, e); 258 else 259 throw new RepositoryException(RepositoryException.DB_ERROR, message + " " + location, e); 260 } 261 catch (SQLException e) 262 { 263 throw new RepositoryException(RepositoryException.DB_ERROR, "JDBC error while executing batch on table " + info.getTableName(), e); 264 } 265 finally 266 { 267 info.reset(); 268 } 269 } 270 271 public void clear() throws SQLException 272 { 273 if (log.isDebugEnabled()) 274 log.debug("Clear batcher\n" + this); 275 statements.clear(); 276 statementLists.clear(); 277 } 278 279 public void reset() throws SQLException 280 { 281 if (log.isDebugEnabled()) 282 log.debug("Reset batcher\n" + this); 283 int max = statementLists.size(); 284 for (int i= 0; i < max; i++) 285 reset(i); 286 } 287 288 public void reset(int i) throws SQLException 289 { 290 Iterator it = ((List)statementLists.get(i)).iterator(); 291 StatementInfo info; 292 while (it.hasNext()) 293 { 294 info = (StatementInfo)it.next(); 295 info.getStatement().clearBatch(); 296 info.reset(); 297 } 298 } 299 300 public void remove(PreparedStatement stmt) 301 { 302 StatementInfo info = (StatementInfo)statements.get(stmt); 303 ((List)statementLists.get(info.getListIndex())).remove(info.getIndex()); 304 statements.remove(stmt); 305 } 306 307 public void close() throws SQLException 308 { 309 if (statements != null) 310 { 311 clear(); 312 statements = null; 313 statementLists = null; 314 conn = null; 315 } 316 } 317 318 private class StatementInfo 319 { 320 private int batchSize; 321 private int batchCount = 0; 322 private List locators = new ArrayList(); 323 private int listIndex; 324 private int indexStatement; 325 private PreparedStatement statement; 326 private String tableName; 327 StatementInfo(int batchSize, int listIndex, int indexStatement, PreparedStatement statement, String tableName) 328 { 329 this.batchSize = batchSize; 330 this.listIndex = listIndex; 331 this.indexStatement = indexStatement; 332 this.statement = statement; 333 this.tableName = tableName; 334 } 335 boolean inc(int line, int column) 336 { 337 if ((line >= 0) || (column >= 0)) 338 locators.add(new LocatorImpl(line, column)); 339 batchCount++; 340 return (autoFlush && (batchCount == batchSize || log.isDebugEnabled())); } 342 void reset() 343 { 344 batchCount = 0; 345 locators.clear(); 346 } 347 LocatorImpl getLocator(int index) 348 { 349 if (index >= locators.size()) 350 return null; 351 else 352 return (LocatorImpl)locators.get(index); 353 } 354 int getCount() 355 {return batchCount;} 356 int getListIndex() 357 {return listIndex;} 358 int getIndex() 359 {return indexStatement;} 360 String getTableName() 361 {return tableName;} 362 PreparedStatement getStatement() 363 {return statement;} 364 public String toString() 365 { 366 return tableName + "\t"+ batchSize + "\t" + batchCount + "\t" + listIndex + "\t" + indexStatement; 367 } 368 } 369 370 private class LocatorImpl 371 { 372 int line; 373 int column; 374 375 LocatorImpl(int line, int column) 376 { 377 this.line = line; 378 this.column = column; 379 } 380 381 public String toString() 382 { 383 return "(line " + line + ", column " + column + ")"; 384 } 385 } 386 } 387 | Popular Tags |