1 19 20 package org.apache.cayenne.dba; 21 22 import java.sql.Connection ; 23 import java.sql.DatabaseMetaData ; 24 import java.sql.ResultSet ; 25 import java.sql.SQLException ; 26 import java.sql.Statement ; 27 import java.sql.Types ; 28 import java.util.ArrayList ; 29 import java.util.Collections ; 30 import java.util.HashMap ; 31 import java.util.Iterator ; 32 import java.util.List ; 33 import java.util.Map ; 34 35 import org.apache.cayenne.CayenneRuntimeException; 36 import org.apache.cayenne.access.DataNode; 37 import org.apache.cayenne.access.OperationObserver; 38 import org.apache.cayenne.access.QueryLogger; 39 import org.apache.cayenne.access.ResultIterator; 40 import org.apache.cayenne.map.DbAttribute; 41 import org.apache.cayenne.map.DbEntity; 42 import org.apache.cayenne.map.DbKeyGenerator; 43 import org.apache.cayenne.query.Query; 44 import org.apache.cayenne.query.SQLTemplate; 45 import org.apache.cayenne.util.IDUtil; 46 47 53 public class JdbcPkGenerator implements PkGenerator { 54 55 public static final int DEFAULT_PK_CACHE_SIZE = 20; 56 57 protected Map pkCache = new HashMap (); 58 protected int pkCacheSize = DEFAULT_PK_CACHE_SIZE; 59 60 public void createAutoPk(DataNode node, List dbEntities) throws Exception { 61 63 if (!autoPkTableExists(node)) { 65 runUpdate(node, pkTableCreateString()); 66 } 67 68 runUpdate(node, pkDeleteString(dbEntities)); 70 71 Iterator it = dbEntities.iterator(); 73 while (it.hasNext()) { 74 DbEntity ent = (DbEntity) it.next(); 75 runUpdate(node, pkCreateString(ent.getName())); 76 } 77 } 78 79 public List createAutoPkStatements(List dbEntities) { 80 List list = new ArrayList (); 81 82 list.add(pkTableCreateString()); 83 list.add(pkDeleteString(dbEntities)); 84 85 Iterator it = dbEntities.iterator(); 86 while (it.hasNext()) { 87 DbEntity ent = (DbEntity) it.next(); 88 list.add(pkCreateString(ent.getName())); 89 } 90 91 return list; 92 } 93 94 97 public void dropAutoPk(DataNode node, List dbEntities) throws Exception { 98 if (autoPkTableExists(node)) { 99 runUpdate(node, dropAutoPkString()); 100 } 101 } 102 103 public List dropAutoPkStatements(List dbEntities) { 104 List list = new ArrayList (); 105 list.add(dropAutoPkString()); 106 return list; 107 } 108 109 protected String pkTableCreateString() { 110 StringBuffer buf = new StringBuffer (); 111 buf 112 .append("CREATE TABLE AUTO_PK_SUPPORT (") 113 .append(" TABLE_NAME CHAR(100) NOT NULL,") 114 .append(" NEXT_ID INTEGER NOT NULL,") 115 .append(" PRIMARY KEY(TABLE_NAME)") 116 .append(")"); 117 118 return buf.toString(); 119 } 120 121 protected String pkDeleteString(List dbEntities) { 122 StringBuffer buf = new StringBuffer (); 123 buf.append("DELETE FROM AUTO_PK_SUPPORT WHERE TABLE_NAME IN ("); 124 int len = dbEntities.size(); 125 for (int i = 0; i < len; i++) { 126 if (i > 0) { 127 buf.append(", "); 128 } 129 DbEntity ent = (DbEntity) dbEntities.get(i); 130 buf.append('\'').append(ent.getName()).append('\''); 131 } 132 buf.append(')'); 133 return buf.toString(); 134 } 135 136 protected String pkCreateString(String entName) { 137 StringBuffer buf = new StringBuffer (); 138 buf 139 .append("INSERT INTO AUTO_PK_SUPPORT") 140 .append(" (TABLE_NAME, NEXT_ID)") 141 .append(" VALUES ('") 142 .append(entName) 143 .append("', 200)"); 144 return buf.toString(); 145 } 146 147 protected String pkSelectString(String entName) { 148 StringBuffer buf = new StringBuffer (); 149 buf.append("SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = '").append( 150 entName).append('\''); 151 return buf.toString(); 152 } 153 154 protected String pkUpdateString(String entName) { 155 StringBuffer buf = new StringBuffer (); 156 buf.append("UPDATE AUTO_PK_SUPPORT").append(" SET NEXT_ID = NEXT_ID + ").append( 157 pkCacheSize).append(" WHERE TABLE_NAME = '").append(entName).append('\''); 158 return buf.toString(); 159 } 160 161 protected String dropAutoPkString() { 162 return "DROP TABLE AUTO_PK_SUPPORT"; 163 } 164 165 168 protected boolean autoPkTableExists(DataNode node) throws SQLException { 169 Connection con = node.getDataSource().getConnection(); 170 boolean exists = false; 171 try { 172 DatabaseMetaData md = con.getMetaData(); 173 ResultSet tables = md.getTables(null, null, "AUTO_PK_SUPPORT", null); 174 try { 175 exists = tables.next(); 176 } 177 finally { 178 tables.close(); 179 } 180 } 181 finally { 182 con.close(); 184 } 185 186 return exists; 187 } 188 189 195 public int runUpdate(DataNode node, String sql) throws SQLException { 196 QueryLogger.logQuery(sql, Collections.EMPTY_LIST); 197 198 Connection con = node.getDataSource().getConnection(); 199 try { 200 Statement upd = con.createStatement(); 201 try { 202 return upd.executeUpdate(sql); 203 } 204 finally { 205 upd.close(); 206 } 207 } 208 finally { 209 con.close(); 210 } 211 } 212 213 223 224 public Object generatePkForDbEntity(DataNode node, DbEntity ent) throws Exception { 225 226 Object binPK = binaryPK(ent); 228 if (binPK != null) { 229 return binPK; 230 } 231 232 DbKeyGenerator pkGenerator = ent.getPrimaryKeyGenerator(); 233 int cacheSize; 234 if (pkGenerator != null && pkGenerator.getKeyCacheSize() != null) 235 cacheSize = pkGenerator.getKeyCacheSize().intValue(); 236 else 237 cacheSize = pkCacheSize; 238 239 if (cacheSize <= 1) { 241 return new Integer (pkFromDatabase(node, ent)); 242 } 243 244 synchronized (pkCache) { 245 PkRange r = (PkRange) pkCache.get(ent.getName()); 246 247 if (r == null) { 248 r = new PkRange(1, 0); 250 pkCache.put(ent.getName(), r); 251 } 252 253 if (r.isExhausted()) { 254 int val = pkFromDatabase(node, ent); 255 r.reset(val, val + cacheSize - 1); 256 } 257 258 return r.getNextPrimaryKey(); 259 } 260 } 261 262 268 protected byte[] binaryPK(DbEntity entity) { 269 List pkColumns = entity.getPrimaryKey(); 270 if (pkColumns.size() == 1) { 271 DbAttribute pk = (DbAttribute) pkColumns.get(0); 272 if (pk.getMaxLength() > 0 273 && (pk.getType() == Types.BINARY || pk.getType() == Types.VARBINARY)) { 274 return IDUtil.pseudoUniqueSecureByteSequence(pk.getMaxLength()); 275 } 276 } 277 278 return null; 279 } 280 281 291 protected int pkFromDatabase(DataNode node, DbEntity ent) throws Exception { 292 String select = "SELECT #result('NEXT_ID' 'int' 'NEXT_ID') " 293 + "FROM AUTO_PK_SUPPORT " 294 + "WHERE TABLE_NAME = '" 295 + ent.getName() 296 + '\''; 297 298 List queries = new ArrayList (2); 300 queries.add(new SQLTemplate(ent, select)); 301 queries.add(new SQLTemplate(ent, pkUpdateString(ent.getName()))); 302 303 PkRetrieveProcessor observer = new PkRetrieveProcessor(ent.getName()); 304 node.performQueries(queries, observer); 305 return observer.getId(); 306 } 307 308 312 public int getPkCacheSize() { 313 return pkCacheSize; 314 } 315 316 327 public void setPkCacheSize(int pkCacheSize) { 328 this.pkCacheSize = (pkCacheSize < 1) ? 1 : pkCacheSize; 329 } 330 331 public void reset() { 332 pkCache.clear(); 333 } 334 335 338 final class PkRetrieveProcessor implements OperationObserver { 339 340 Number id; 341 String entityName; 342 343 PkRetrieveProcessor(String entityName) { 344 this.entityName = entityName; 345 } 346 347 public boolean isIteratedResult() { 348 return false; 349 } 350 351 public int getId() { 352 if (id == null) { 353 throw new CayenneRuntimeException("No key was retrieved for entity " 354 + entityName); 355 } 356 357 return id.intValue(); 358 } 359 360 public void nextDataRows(Query query, List dataRows) { 361 362 if (dataRows == null || dataRows.size() == 0) { 364 throw new CayenneRuntimeException( 365 "Error generating PK : entity not supported: " + entityName); 366 } 367 368 if (dataRows.size() > 1) { 369 throw new CayenneRuntimeException( 370 "Error generating PK : too many rows for entity: " + entityName); 371 } 372 373 Map lastPk = (Map ) dataRows.get(0); 374 id = (Number ) lastPk.get("NEXT_ID"); 375 } 376 377 public void nextCount(Query query, int resultCount) { 378 if (resultCount != 1) { 379 throw new CayenneRuntimeException("Error generating PK for entity '" 380 + entityName 381 + "': update count is wrong - " 382 + resultCount); 383 } 384 } 385 386 public void nextBatchCount(Query query, int[] resultCount) { 387 } 388 389 public void nextGeneratedDataRows(Query query, ResultIterator keysIterator) { 390 } 391 392 public void nextDataRows(Query q, ResultIterator it) { 393 } 394 395 public void nextQueryException(Query query, Exception ex) { 396 397 throw new CayenneRuntimeException("Error generating PK for entity '" 398 + entityName 399 + "'.", ex); 400 } 401 402 public void nextGlobalException(Exception ex) { 403 404 throw new CayenneRuntimeException("Error generating PK for entity: " 405 + entityName, ex); 406 } 407 } 408 } 409 | Popular Tags |