SQLStoreManagerpublic class SQLStoreManager extends Object implements PersistenceStoreThis class connects to a persistent store. It supports
relational databases such as Oracle and MS SQLServer. This class
knows how to generate SQL statements to access and manipulate
objects stored in a relational database. |
Fields Summary |
---|
private ConfigCache | configCacheCache holding SQLStore model information. | private com.sun.jdo.spi.persistence.support.sqlstore.database.DBVendorType | vendorTypeEncapsulates database type. | private static com.sun.jdo.spi.persistence.utility.logging.Logger | loggerThe logger. | private static com.sun.jdo.spi.persistence.utility.logging.Logger | sqlLoggerThe sql logger. | private static final ResourceBundle | messagesI18N message handler. | private static int | fetchSizeFetch size for query statements. |
Constructors Summary |
---|
public SQLStoreManager(DatabaseMetaData databaseMetaData, String identifier)
super();
configCache = new ConfigCacheImpl();
setVendorType(databaseMetaData, identifier);
|
Methods Summary |
---|
private static void | close(java.sql.ResultSet r)Closes the JDBC ResultSet r .
SQLExceptions are catched and logged.
if (r != null) {
try {
r.close();
} catch (SQLException ex) {
// only log exception
logger.finest(I18NHelper.getMessage(messages,
"sqlstore.sqlstoremanager.errorcloseresultset", // NOI18N
ex.getLocalizedMessage()));
}
}
| private static void | close(DBStatement s)Closes the JDBC Statement s .
SQLExceptions are catched and logged.
if (s != null) {
try {
s.close();
} catch (SQLException ex) {
// only log exception
logger.finest(I18NHelper.getMessage(messages,
"sqlstore.sqlstoremanager.errorclosestatement", // NOI18N
ex.getLocalizedMessage()));
}
}
| private static void | closeConnection(Transaction t, java.sql.Connection c)Delegates the closure of the JDBC connection c
to the transaction t .
if (t != null && c != null) {
t.releaseConnection();
}
| private static void | closeDBStatements(UpdateQueryPlan plan, Transaction tran)Removes all DBStatements for specified plan and closes the JDBC Statement
wrapped by the DBStatement.
if ((plan != null) && (tran != null)) {
for (Iterator i = plan.getStatements().iterator(); i.hasNext(); ) {
UpdateStatement updateStmt = (UpdateStatement)i.next();
DBStatement s = updateStmt.removeDBStatement(tran);
close(s);
}
}
| public void | execute(PersistenceManager pm, java.util.Collection actions)Executes the list of SQL requests contained in actions .
Requests can be INSERT, UPDATE or DELETE operations.
Iterator iter = actions.iterator();
while (iter.hasNext()) {
ActionDesc action = (ActionDesc) iter.next();
if (action instanceof UpdateObjectDescImpl) {
UpdateObjectDescImpl request = (UpdateObjectDescImpl) action;
UpdateQueryPlan plan = new UpdateQueryPlan(request, this);
plan.build();
for (int i = 0, size = plan.statements.size(); i < size; i++) {
UpdateStatement s = (UpdateStatement) plan.statements.get(i);
if (s != null) {
executeUpdate(pm, s, request);
}
}
} else {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.generic.notinstanceof", // NOI18N
action.getClass().getName(),
"UpdateObjectDescImpl")); // NOI18N
}
}
| public void | executeBatch(PersistenceManager pm, UpdateObjectDesc request, boolean forceFlush)Retrieves the update query plan for the specified request and
calls executeUpdateBatch for all statements in this plan.
boolean cleanup = true;
UpdateObjectDescImpl objectRequest = null;
if (request instanceof UpdateObjectDescImpl) {
objectRequest = (UpdateObjectDescImpl) request;
} else {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.generic.notinstanceof", // NOI18N
request.getClass().getName(),
"UpdateObjectDescImpl")); // NOI18N
}
ClassDesc config = objectRequest.getConfig();
UpdateQueryPlan plan = config.getUpdateQueryPlan(objectRequest, this);
Transaction tran = (Transaction) pm.currentTransaction();
Connection conn = tran.getConnection();
// Flag 'doFlush' indicates that executeUpdateBatch should call
// executeBatch on the PreparedStatement. 'doFlush' is true, if
// - forceFlush is true, this means we cannot reuse the
// DBStatement for the next state manager, OR
// - the batch threshold is exeeceded.
boolean doFlush = forceFlush || plan.checkBatchThreshold(tran);
try {
for (int i = 0, size = plan.statements.size(); i < size; i++) {
UpdateStatement s = (UpdateStatement) plan.statements.get(i);
executeUpdateBatch(tran, conn, s, objectRequest, doFlush);
}
// At this point we know the batch update was successful.
// We close DBStatements and it's related PreparedStatement, if
// they are not going to be reused for flushing the next state
// manager. In this case the flag 'forceFlush' is true. So we
// can do the cleanup, if 'forceFlush' is true. We cannot use
// the flag 'doFlush', because this flag might be true, because
// we exceeded the batch threshold. In this case we want to
// keep the DBStatement.
cleanup = forceFlush;
} finally {
if (cleanup)
closeDBStatements(plan, tran);
closeConnection(tran, conn);
}
| private java.lang.Object | executeQuery(PersistenceManager pm, SelectStatement statement, com.sun.jdo.spi.persistence.support.sqlstore.sql.concurrency.Concurrency concurrency, ValueFetcher parameters)The executeQuery method prepares and sends the SQL text contained in the
statement to the DB. Data used for placeholders in the
statement is passed in parameters and bound to
the statement before execution. The SQL statement is run within
a cursor and any result values returned are packaged into
BusinessClasses of the appropriate sub-class and returned as the
functional result.
Object result = null;
boolean debug = logger.isLoggable();
if (debug) {
logger.fine("sqlstore.sqlstoremanager.executeQuery"); // NOI18N
}
String sqlText = statement.getText();
if (sqlText.length() > 0) {
if (sqlLogger.isLoggable()) {
sqlLogger.fine(statement.getFormattedSQLText(parameters));
}
Transaction tran = null;
if (concurrency != null) {
// This is a no op currently as all Concurrency* classes
// have no code in this method and always returns null.
tran = concurrency.suspend();
}
if (tran == null) {
tran = (Transaction) pm.currentTransaction();
}
ResultSet resultData = null;
DBStatement s = null;
Connection conn = tran.getConnection();
try {
// prepare Statement including SELECT Statement timeout
s = new DBStatement(conn, sqlText, tran.getQueryTimeout());
// Set the inputValues values (constraints in this case).
statement.bindInputValues(s, parameters);
// Tests setting the fetch size with values 0, 16, 32, 64,
// showed degradations only.
//s.handle.setFetchSize(<fetch_size>);
//s.handle.setFetchDirection(ResultSet.FETCH_FORWARD);
if (fetchSize > -1) {
s.getPreparedStatement().setFetchSize(fetchSize);
}
if (statement.isColumnTypeDefinitionNeeded()) {
vendorType.getSpecialDBOperation().defineColumnTypeForResult(
s.getPreparedStatement(), statement.getColumnRefs());
}
resultData = s.executeQuery();
if (concurrency != null) {
// This is a no op currently as all Concurrency* classes
// have no code in this method.
concurrency.resume(tran);
}
SelectQueryPlan plan = (SelectQueryPlan) statement.getQueryPlan();
result = plan.getResult(pm, resultData);
} catch (SQLException e) {
throwJDOSqlException(e, statement.getFormattedSQLText(parameters));
} finally {
close(resultData);
close(s);
closeConnection(tran, conn);
}
}
if (debug) {
logger.fine("sqlstore.sqlstoremanager.executeQuery.exit"); // NOI18N
}
return result;
| private void | executeUpdate(PersistenceManager pm, UpdateStatement updateStatement, com.sun.jdo.spi.persistence.support.sqlstore.sql.UpdateObjectDescImpl updateDesc)Executes the SQL text contained in updateStatement
against the database. Data used for placeholders in the
updateStatement is passed in the updateDesc parameter
and bound to the statement before execution. Can be used for
delete, insert, or update SQL statements, but not for select
SQL statements as these return result values with which
executeUpdate is not prepared to deal.
int affectedRows = 0;
boolean debug = logger.isLoggable();
if (debug) {
logger.fine("sqlstore.sqlstoremanager.executeupdate"); // NOI18N
}
String sqlText = updateStatement.getText();
if (sqlText.length() > 0) {
if (sqlLogger.isLoggable()) {
sqlLogger.fine(updateStatement.getFormattedSQLText());
}
Transaction tran = (Transaction) pm.currentTransaction();
Connection conn = tran.getConnection();
DBStatement s = null;
boolean preparationSuccessful = false;
try {
// Statement preparation.
s = new DBStatement(conn, sqlText, tran.getUpdateTimeout());
updateStatement.bindInputValues(s);
preparationSuccessful = true;
// Excecution.
affectedRows = s.executeUpdate();
// If the affectedRows is less than the minimum rows required,
// we need to abort and throw an exception.
if (affectedRows < updateStatement.minAffectedRows) {
// Mark the request failed.
updateDesc.setVerificationFailed();
rollbackXact(tran);
throwJDOConcurrentAccessException(sqlText);
}
} catch (SQLException e) {
// As we want to verify against the data store only,
// there is no invalidation necessary if the
// exception happened before statement execution.
if (preparationSuccessful) {
updateDesc.setVerificationFailed();
}
rollbackXact(tran);
throwJDOSqlException(e, updateStatement.getFormattedSQLText());
} finally {
close(s);
closeConnection(tran, conn);
}
}
if (debug) {
logger.fine("sqlstore.sqlstoremanager.executeupdate.exit", // NOI18N
new Integer(affectedRows));
}
| private void | executeUpdateBatch(Transaction tran, java.sql.Connection conn, UpdateStatement updateStatement, com.sun.jdo.spi.persistence.support.sqlstore.sql.UpdateObjectDescImpl updateDesc, boolean doFlush)Binds the specified update descriptor to the specified statement and
calls method addBatch on this statement.
int[] affectedRows = null;
boolean debug = logger.isLoggable();
if (debug) {
logger.fine("sqlstore.sqlstoremanager.executeupdatebatch"); // NOI18N
}
String sqlText = updateStatement.getText();
if (sqlText.length() > 0) {
if (sqlLogger.isLoggable()) {
String formattedText = updateStatement.getFormattedSQLText(updateDesc);
if (doFlush) {
sqlLogger.fine("sqlstore.sqlstoremanager.executeupdatebatch.flushbatch", formattedText);
} else {
sqlLogger.fine("sqlstore.sqlstoremanager.executeupdatebatch.addbatch", formattedText);
}
}
DBStatement s = null;
try {
// Batch preparation.
s = updateStatement.getDBStatement(tran, conn);
updateStatement.bindInputColumns(s, updateDesc);
s.addBatch();
if (doFlush) {
// Execution.
affectedRows = s.executeBatch();
// check affectedRows as returned by the database
for (int i = 0; i < affectedRows.length; i++) {
// If the affectedRows is less than the minimum rows required,
// we need to abort and throw an exception.
if (affectedRows[i] < updateStatement.minAffectedRows &&
affectedRows[i] != java.sql.Statement.SUCCESS_NO_INFO) {
rollbackXact(tran);
throwJDOConcurrentAccessException(sqlText);
}
}
}
} catch (SQLException e) {
rollbackXact(tran);
throwJDOSqlException(e, sqlText);
}
}
if (debug) {
if (doFlush) {
logger.fine("sqlstore.sqlstoremanager.executeupdatebatch.exit.flush", '[" + // NOI18N
StringHelper.intArrayToSeparatedList(affectedRows, ",") + ']"); //NOI18N
} else {
logger.fine("sqlstore.sqlstoremanager.executeupdatebatch.exit"); //NOI18N
}
}
| public java.lang.Class | getClassByOidClass(java.lang.Class oidType)
return configCache.getClassByOidClass(oidType);
| public ConfigCache | getConfigCache()
return configCache;
| public PersistenceConfig | getPersistenceConfig(java.lang.Class classType)Returns the sqlstore model for class classType .
Sqlstore model information is cached. If the model cache does
not already hold a model instance for the particular class,
a new instance is created, initialized and put into the cache.
The access to the model cache is synchronized. // -1 not set
if (logger.isLoggable(Logger.FINER)) {
logger.finer("sqlstore.sqlstoremanager.getpersistenceconfig",
classType.getName()); // NOI18N
}
return configCache.getPersistenceConfig(classType);
| public RetrieveDesc | getRetrieveDesc(java.lang.Class classType)Returns a new retrieve descriptor for anexternal (user) query.
return new RetrieveDescImpl(classType, (ClassDesc) getPersistenceConfig(classType));
| public RetrieveDesc | getRetrieveDesc(java.lang.String fieldName, java.lang.Class classType)Returns a new retrieve descriptor for anexternal (user) query.
This retrieve descriptor can be used to query for the foreign
field name .
ClassDesc c = (ClassDesc) getPersistenceConfig(classType);
if (c != null) {
FieldDesc f = c.getField(fieldName);
if (f instanceof ForeignFieldDesc) {
ForeignFieldDesc ff = (ForeignFieldDesc) f;
return getRetrieveDesc(ff.foreignConfig.getPersistenceCapableClass());
}
}
return null;
| public StateManager | getStateManager(java.lang.Class classType)
ClassDesc c = (ClassDesc) getPersistenceConfig(classType);
if (c != null) {
return c.newStateManagerInstance(this);
}
return null;
| public UpdateObjectDesc | getUpdateObjectDesc(java.lang.Class classType)
return new UpdateObjectDescImpl(classType);
| public com.sun.jdo.spi.persistence.support.sqlstore.database.DBVendorType | getVendorType()
return vendorType;
| public java.lang.Object | retrieve(PersistenceManager pm, RetrieveDesc action, ValueFetcher parameters)The retrieve method builds and executes the SQL query described by
the action parameter.
if (action == null) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.generic.nullparam", "action")); // NOI18N
}
if (!(action instanceof RetrieveDescImpl)) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.generic.notinstanceof", // NOI18N
action.getClass().getName(),
"RetrieveDescImpl")); // NOI18N
}
RetrieveDescImpl retrieveAction = ((RetrieveDescImpl) action);
ClassDesc config = retrieveAction.getConfig();
Concurrency concurrency = config.getConcurrency(pm.isOptimisticTransaction());
SelectQueryPlan plan = retrieveAction.buildQueryPlan(this, concurrency);
ArrayList statements = plan.getStatements();
Object result = null;
SelectStatement s = (SelectStatement) statements.get(0);
result = executeQuery(pm, s, concurrency, parameters);
if ((plan.options & RetrieveDescImpl.OPT_AGGREGATE) == 0) {
// This was a regular query, no aggregate.
if ((plan.options & RetrieveDescImpl.OPT_DISTINCT) > 0) {
// Perform manual DISTINCT if required
if (((plan.options & RetrieveDescImpl.OPT_FOR_UPDATE) > 0 &&
!vendorType.isDistinctSupportedWithUpdateLock()) ) {
HashSet hash = new HashSet();
for (Iterator iter = ((Collection)result).iterator(); iter.hasNext(); ) {
Object temp = iter.next();
if (!hash.contains(temp)) {
hash.add(temp);
} else {
iter.remove();
}
}
}
}
}
return result;
| private void | rollbackXact(Transaction tran)
try {
tran.setRollbackOnly();
//tran.rollback();
} catch (Exception e) {
}
| private void | setVendorType(java.sql.DatabaseMetaData databaseMetaData, java.lang.String identifier)
try {
vendorType = new DBVendorType(databaseMetaData, identifier);
if (logger.isLoggable()) {
logger.fine("sqlstore.sqlstoremanager.vendortype",vendorType.getName()); // NOI18N
}
} catch (Exception e) {
if (e instanceof JDOException) {
throw (JDOException) e;
} else {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.configuration.getvendortypefailed"), e); // NOI18N
}
}
| private static void | throwJDOConcurrentAccessException(java.lang.String sqlText)Determines the SQL operation (update/delete) from the
sqlText parameter and throws a JDODataStoreException.
String operation = sqlText.substring(0, sqlText.indexOf(' ")); // NOI18N
throw new JDODataStoreException(I18NHelper.getMessage(messages,
"core.store.concurrentaccess", operation)); // NOI18N
| private static void | throwJDOSqlException(java.sql.SQLException e, java.lang.String sqlText)Constructs the exception message including the executed SQL statement
sqlText and throws a JDODataStoreException passing the
original exception.
String exceptionMessage = I18NHelper.getMessage(messages,
"core.persistencestore.jdbcerror", sqlText); // NOI18N
throw new JDODataStoreException(exceptionMessage, e);
|
|