📄 sqlcache.java
字号:
/*
* (c) Copyright 2002, 2003, 2004, 2005, 2006, 2007 Hewlett-Packard Development Company, LP
* All rights reserved.
*
*/
//=======================================================================
// Package
package com.hp.hpl.jena.db.impl;
//=======================================================================
// Imports
import java.sql.*;
import java.util.*;
import java.io.*;
import com.hp.hpl.jena.db.*;
import com.hp.hpl.jena.shared.JenaException;
import com.hp.hpl.jena.util.CollectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//=======================================================================
/**
* Stores a set of sql statements loaded from a resource file.
* Caches prepared versions of the statements for a given db connection.
* <p>
* The resource file is located on the classpath and has the format:
* <pre>
* # comment at start of line
* operationName1
* sql code line 1
* ...
* sql code last line
*
* operationName2
* ...
* </pre>
* where the blank lines delimit one sql block from the next.
* <p>The sql code is typically a single SQL statement but some operations,
* specifically database initialization and cleanup may require a variable number
* of statments. To cater for this terminate each statement in those groups with
* the string ";;". Note that a single ";" is not used because these compound
* statements are often stored procedure definitions which end to have ";" line
* terminators!
*
* @author <a href="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>. Updated by hkuno to support GraphRDB.
* @version $Revision: 1.17 $ on $Date: 2007/01/02 11:50:42 $
*/
public class SQLCache {
//=======================================================================
// Variables
/** Set of sql statements indexed by operation name. */
protected Properties m_sql;
/** Cache of prepared versions of the statements. Each map entry is a list
* of copies of the prepared statement for multi-threaded apps. */
protected Map m_preparedStatements = CollectionFactory.createHashedMap();
/** Track which cached, prepared statements are in use and the corresponding
* list to which the statement should be returned. */
protected Map m_cachedStmtInUse = CollectionFactory.createHashedMap();
/** the packaged jdbc connection to the database itself. */
protected IDBConnection m_connection;
/** Maximum number of pre-prepared statements to keep for each operator. */
protected static final int MAX_PS_CACHE = 4;
/** Set to true to enable cache of pre-prepared statements. */
protected boolean CACHE_PREPARED_STATEMENTS = true;
static protected Log logger = LogFactory.getLog( SQLCache.class );
//=======================================================================
// Public interface
/**
* Constructor. Creates a new cache sql statements for interfacing to
* a specific database.
* @param sqlFile the name of the file of sql statements to load, this is
* loaded from the classpath.
* @param defaultOps Properties table which provides the default
* sql statements, any definitions of a given operation in the loaded file
* will override the default.
* @param connection the jdbc connection to the database itself
* @param idType the sql string to use for id types (substitutes for $id in files)
*/
public SQLCache(String sqlFile, Properties defaultOps, IDBConnection connection, String idType) throws IOException {
m_sql = loadSQLFile(sqlFile, defaultOps, idType);
m_connection = connection;
}
/**
* Set to true to enable cache of pre-prepared statements.
*/
public void setCachePreparedStatements(boolean state) {
CACHE_PREPARED_STATEMENTS = state;
}
/**
* Return true if cache of pre-prepared statements is enabled.
*/
public boolean getCachePreparedStatements() {
return CACHE_PREPARED_STATEMENTS;
}
/**
* Flush the cache of all currently prepared statements.
*/
public void flushPreparedStatementCache() throws RDFRDBException {
try {
Iterator it = m_preparedStatements.values().iterator();
while (it.hasNext()) {
Iterator psit = ((List)it.next()).iterator();
while (psit.hasNext()) {
((PreparedStatement)psit.next()).close();
}
}
} catch (SQLException e) {
throw new RDFRDBException("Problem flushing PS cache", e);
} finally {
m_preparedStatements = CollectionFactory.createHashedMap();
m_cachedStmtInUse = CollectionFactory.createHashedMap();
}
}
/**
* Return the associated jdbc connection.
*/
public Connection getConnection() throws SQLException {
return m_connection.getConnection();
}
/**
* Set the associated jdbc connection.
*/
public void setConnection(IDBConnection connection) {
m_connection = connection;
}
/**
* Return the raw SQL statement corresponding to the named operation.
*/
public String getSQLStatement(String opname) throws SQLException {
return getSQLStatement(opname, (String[]) null);
}
/**
* Return the raw SQL statement corresponding to the named operation.
* Substitute the ${a} attribute macro for the current attribute number.
*/
public String getSQLStatement(String opname, String[] attr) throws SQLException {
String cmd = m_sql.getProperty(opname);
if (cmd == null) {
if ( opname.startsWith("*") ) {
cmd = genSQLStatement(opname);
m_sql.setProperty(opname, cmd);
} else {
logger.error("Unable to find SQL for operation: " + opname);
throw new SQLException("Unable to find SQL for operation: " + opname);
}
}
int attrCnt = (attr == null) ? 0 : attr.length;
if ( attrCnt > 0 ) cmd = substitute(cmd, "${a}", attr[0]);
if ( attrCnt > 1 ) cmd = substitute(cmd, "${b}", attr[1]);
if ( attrCnt > 2 ) cmd = substitute(cmd, "${c}", attr[2]);
if ( attrCnt > 3 ) throw new JenaException("Too many arguments");
return cmd;
}
public String getSQLStatement(String opname, String attr) throws SQLException {
String[] param = {attr};
return getSQLStatement(opname,param);
}
/**
* Return the raw SQL statement corresponding to the named operation.
* Attribute version - substitute the ${a} attribute macro for
* the current attribute number.
*/
public String getSQLStatement(String opname, String attrA, String attrB) throws SQLException {
String[] param = {attrA,attrB};
return getSQLStatement(opname,param);
}
/**
* Return a set of raw SQL statements corresponding to the named operation.
* This is used for compound operations where more than one SQL command is needed to
* implement the operation (e.g. database formating and clean up). The
* individual statements should be separated by double-semicolons at the end of the line.
* <p>Needs refactoring to clarify what operations are and are not compound but for now
* it is assumed the caller knows which is correct. Compound statements are not called
* repeatedly so don't currently cache the parsed statement set.
*/
public Collection getSQLStatementGroup(String opname) throws SQLException {
String statementSrc = m_sql.getProperty(opname);
if (statementSrc == null) {
throw new SQLException("Unable to find SQL for operation: " + opname);
}
int start = 0;
int split = 0;
List statements = new LinkedList();
while (split != -1) {
split = statementSrc.indexOf(";;\n", start);
String statement = null;
if (split == -1) {
statement = statementSrc.substring(start);
} else {
statement = statementSrc.substring(start, split);
start = split +2;
}
if (!statement.trim().equals(""))
statements.add(statement);
}
return statements;
}
/**
* Return a prepared SQL statement corresponding to the named operation.
* The statement should either be closed after use or returned to the
* prepared statement pool using {@link #returnPreparedSQLStatement returnPreparedSQLStatement}
*
* <p>Only works for single statements, not compound statements.
* @param con the jdbc connection to use for preparing statements
* @param opname the name of the sql operation to locate
* @return a prepared SQL statement appropriate for the JDBC connection
* used when this SQLCache was constructed or null if there is no such
* operation or no such connection
*
*
*/
public synchronized PreparedStatement getPreparedSQLStatement(String opname, String [] attr) throws SQLException {
/* TODO extended calling format or statement format to support different
* result sets and conconcurrency modes.
*/
PreparedStatement ps=null;
if (m_connection == null || opname == null) return null;
int attrCnt = (attr == null) ? 0 : attr.length;
String aop = opname;
if ( attrCnt > 0 ) aop = concatOpName(aop, attr[0]);
if ( attrCnt > 1 ) aop = concatOpName(aop, attr[1]);
if ( attrCnt > 2 ) aop = concatOpName(aop, attr[2]);
if ( attrCnt > 3 ) throw new JenaException("Too many arguments");
List psl = (List) m_preparedStatements.get(aop);
// OVERRIDE: added proper PreparedStatement removal.
if (psl!=null && !psl.isEmpty()) {
ps = (PreparedStatement) psl.remove(0);
try{
ps.clearParameters();
}catch(SQLException e) {
ps.close();
}
}
if (ps == null) {
String sql = getSQLStatement(opname, attr);
if (sql == null) {
throw new SQLException("No SQL defined for operation: " + opname);
}
if (psl == null && CACHE_PREPARED_STATEMENTS) {
psl = new LinkedList();
m_preparedStatements.put(aop, psl);
}
ps = doPrepareSQLStatement(sql);
}
if ( CACHE_PREPARED_STATEMENTS ) m_cachedStmtInUse.put(ps,psl);
return ps;
}
/**
* Prepare a SQL statement for the given statement string.
*
* <p>Only works for single statements, not compound statements.
* @param stmt the sql statement to prepare.
* @return a prepared SQL statement appropriate for the JDBC connection
* used when this SQLCache was constructed or null if there is no such
* connection.
*/
private synchronized PreparedStatement doPrepareSQLStatement(String sql) throws SQLException {
if (m_connection == null) return null;
return getConnection().prepareStatement(sql);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -