All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.genexus.db.driver.CursorFactory Maven / Gradle / Ivy

Go to download

Core classes for the runtime used by Java and Android apps generated with GeneXus

There is a newer version: 4.7.3
Show newest version

package com.genexus.db.driver;
import java.io.PrintStream;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Hashtable;

/**
 * Manejo de pool de cursores preparados para una conexi�n.
 * 

* Se puede definir el m�ximo numero de cursores por conexi�n. *

* Cada conexion a un data source tiene su CursorFactory. * * @author Andres Aguiar * @see com.genexus.ConnectionManager#getCursorFactory */ import com.genexus.DebugFlag; final class CursorFactory implements IPreparedStatementCache { private static final boolean DEBUG = DebugFlag.DEBUG; String arrayIds[]; SQLCursorData data[]; long timeStamps[]; int maxSize; GXConnection jdbcConnection; Hashtable preparedStatements; private int usedCursors; //Hashtable statementsPerHandle; /** * Construye un nuevo CursorFactory con un tama�o m�ximo dado * para una conexi�n JDBC dada. */ CursorFactory(int maxSize, GXConnection jdbcConnection) { if (maxSize > 2) maxSize --; usedCursors = 0; arrayIds = new String[maxSize]; timeStamps = new long[maxSize]; data = new SQLCursorData[maxSize]; this.maxSize = maxSize; preparedStatements = new Hashtable<>(); //statementsPerHandle = new Hashtable(); this.jdbcConnection = jdbcConnection; } public synchronized int getUsedCursors() { return usedCursors; } public int getUsedCursorsJMX() { return usedCursors; } /** * Hace un drop del cursor que hace mas tiempo no se usa. */ private int dropOlderCursor() throws SQLException { long minTimeStamp = Long.MAX_VALUE; int index = -1; for(int i = maxSize - 1; i >= 0; i--) { if (timeStamps[i] < minTimeStamp && timeStamps[i] != 0 && !data[i].isInUse()) { minTimeStamp = timeStamps[i]; index = i; } } if (index == -1) { System.err.println("Error can't prepare more cursors \n You should increase the Maximum cursors per connection preference"); if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "Error can't prepare more cursors \n You should increase the Maximum cursors per connection preference"); throw new InternalError("Error can't prepare more cursors \n You should increase the Maximum cursors per connection preference"); } // Hago el drop del cursor y reseteo elementos de los arrays. // try // { data[index].getPreparedStatement().close(); preparedStatements.remove(data[index].getPreparedStatement()); // } // catch (SQLException e) // { // PrivateUtilities.errorHandler("SQL Exception CursorFactory/dropOlderCursor " + e.getErrorCode() + "-" + e.getSQLState(), e); // } if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "GX: Dropping cursor " + arrayIds[index] + " - " + data[index].getSQLSentence()); data[index] = null; arrayIds[index] = null; timeStamps[index] = 0; return index; } /** * Agrega un nuevo cursor en el pool. */ private PreparedStatement addElement(int handle, String cursorId, String sqlSentence, boolean currentOf, boolean callable) throws SQLException { int i; PreparedStatement statement = null; if (maxSize == 0) { if (callable) return jdbcConnection.prepareCall(sqlSentence); else return jdbcConnection.prepareStatement(sqlSentence, handle, cursorId); } // Obtengo el siguiente valor con clave = 0. for(i = 0; i < maxSize && arrayIds[i] != null; ++i); // Si no hay ninguno, hago drop del cursor mas viejo y me quedo con su �ndice. if (i == maxSize) { i = dropOlderCursor(); } if (callable) statement = jdbcConnection.prepareCall(sqlSentence); else statement = jdbcConnection.prepareStatement(sqlSentence, handle, cursorId); arrayIds[i] = cursorId; timeStamps[i] = System.currentTimeMillis(); data[i] = new SQLCursorData(sqlSentence, statement); preparedStatements.put(statement, data[i]); if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "GX: Preparing new cursor " + cursorId + " - " + sqlSentence); if (currentOf && jdbcConnection.getDBMS() instanceof GXDBMSas400) { try { statement.setCursorName(cursorId); } catch(SQLException e) { data[i] = null; arrayIds[i] = null; timeStamps[i] = 0; preparedStatements.remove(statement); throw e; } } usedCursors++; ((GXPreparedStatement) statement).setHandle(handle); return statement; } /* void addStatementToHandle(int handle, ResultSet rslt) { Hashtable statements = (Hashtable) statementsPerHandle.get(new Integer(handle)); if (statements == null) { statements = new Hashtable(); statementsPerHandle.put(new Integer(handle), statements); } statements.put(rslt, ""); } void removeStatementFromHandle(int handle, ResultSet rslt) { Hashtable statements = (Hashtable) statementsPerHandle.get(new Integer(handle)); statements.remove(rslt); } */ public CallableStatement getCallableStatement(int handle, String index, String sqlSentence) throws SQLException { return (CallableStatement) getStatement(handle, index, sqlSentence, false, true, false); } /** * Devuelve una sentencia preparada para el indice pasado como * par�metro. Si ya esta preparada, la devuelve, sino la prepara * y la devuelve */ public PreparedStatement getStatement(int handle, String index, String sqlSentence, boolean currentOf) throws SQLException { return getStatement(handle, index, sqlSentence, currentOf, false, false); } private void reprepareStatement(int handle, String index, String sqlSentence, boolean currentOf, boolean callable, int arrIdx) throws SQLException { data[arrIdx].getPreparedStatement().close(); timeStamps[arrIdx] = System.currentTimeMillis(); data[arrIdx] = new SQLCursorData(sqlSentence, callable?jdbcConnection.prepareCall(sqlSentence):jdbcConnection.prepareStatement(sqlSentence, handle, index)); usedCursors++; if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "GX: Re preparing new cursor " + index+ " - " + sqlSentence); } public synchronized PreparedStatement getStatement(int handle, String index, String sqlSentence, boolean currentOf, boolean callable, boolean batch) throws SQLException { int i; // Busco secuencialmente por el Id. for (i = 0; i < maxSize && (arrayIds[i] == null || !(arrayIds[i].equals(index) && data[i].getSQLSentence().equals(sqlSentence)) || data[i].isInUse()); i++); if (i == maxSize) { return addElement(handle, index, sqlSentence, currentOf, callable); } // Si la sentencia SQL que esta en el array es distinta a la que quiero preparar, // es porque tengo el mismo ID para otra sentencia, lo que no deberia ser posible. if (!data[i].getSQLSentence().equals(sqlSentence)) { reprepareStatement(handle, index, sqlSentence, currentOf, callable, i); /* Desde 1/10/02 la sentencia puede cambiar por causa de contraints o orders condicionales if (com.genexus.ApplicationContext.getInstance().isServletEngine()) { System.err.println("Warning - Repreparing the statement " + index); reprepareStatement(handle, index, sqlSentence, currentOf, callable, i); } else { System.err.println("The prepared cursor has a different SQL statement. You are probably \nmixing sources from different GeneXus KB\n"); if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "The prepared cursor has a different SQL statement. You are probably \nmixing sources from different GeneXus KB\n"); throw new InternalError(); } */ } if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "GX: Reusing cursor " + index + " - " + sqlSentence + " MaxSize " + maxSize); // Actualizo el timestamp timeStamps[i] = System.currentTimeMillis(); usedCursors++; ((GXPreparedStatement) data[i].getPreparedStatement()).setHandle(handle); return data[i].getPreparedStatement(); } /** * Devuelve una sentencia preparada para la tabla/operacion/sentencia * pasada como par�metro. Si ya esta preparada, la devuelve, sino * la prepara y la devuelve */ public synchronized PreparedStatement getStatement(int handle, int tableId, int operationId, String sqlSentence) throws SQLException { // Busco secuencialmente por el Id y por sentencia SQL. int i; String index = CursorFactoryConstants.calculateCursorId(tableId, operationId); for (i = 0; i < maxSize && (arrayIds[i] == null || !(arrayIds[i].equals(index) && data[i].getSQLSentence().equals(sqlSentence) && !data[i].isInUse())); i++); if (i == maxSize) { return addElement(handle, index, sqlSentence, false, false); } // Actualizo el timestamp timeStamps[i] = System.currentTimeMillis(); if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "GX: Reusing cursor TBL " + index + " - " + sqlSentence); usedCursors++; ((GXPreparedStatement) data[i].getPreparedStatement()).setHandle(handle); return data[i].getPreparedStatement(); } /** * Hace un drop de todos los cursores preparados del pool de cursores. */ public synchronized void dropAllCursors() { for (int i = maxSize - 1; i >= 0; i--) { if (arrayIds[i] != null) { if(DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "GX: Drop All/Dropping cursor " + arrayIds[i] + " - " + data[i].getSQLSentence()); try { data[i].getPreparedStatement().close(); } catch (SQLException e) { if(DEBUG) { jdbcConnection.log(GXDBDebug.LOG_MED, "Error dropping cursors " + arrayIds[i] + " - " + data[i].getSQLSentence()); jdbcConnection.logSQLException(-1, e); } } data[i] = null; arrayIds[i] = null; timeStamps[i] = 0; } } preparedStatements.clear(); usedCursors = 0; } public synchronized void freeAllCursors() { for (int i = maxSize - 1; i >= 0; i--) { if (arrayIds[i] != null && data[i].isInUse()) { data[i].setNotInUse(); } } usedCursors = 0; } /** Elimina un cursor (lo cierra y lo saca de la lista de prepared statements) */ public synchronized void dropCursor(GXPreparedStatement stmt) { if(stmt == null)return; setNotInUse(stmt); try { stmt.close(); } catch (SQLException e){ ; } preparedStatements.remove(stmt); return; } /** * Marca un cursor como "Not in use", de modo que pueda ser reutilizado. */ public synchronized void setNotInUse(GXPreparedStatement stmt) { // Puede venir en null si se llama desde un supplier que nunca // abri� el cursor. if (stmt == null) return; if (maxSize == 0) { try { stmt.close(); } catch (SQLException e) { System.err.println("can't close cursor"); } return; } SQLCursorData data = preparedStatements.get(stmt); if (data != null) { data.setNotInUse(); usedCursors --; if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "GX: Setting cursor as unused " + data.getSQLSentence() + "/" + data.statement); } else { if (DEBUG) jdbcConnection.log(GXDBDebug.LOG_MED, "GX: Warning, trying to mark as unused an inexistent cursor"); } } public void dump(PrintStream out) { out.println("\tPreparedStatements : " + usedCursors + "/" + maxSize); for (int i = 0; i < maxSize; i++) { if (arrayIds[i] != null) { out.println("\t\t" + i + " id: " + data[i].statement.hashCode() + " - gxid: " + arrayIds[i] + " - inuse: " + data[i].inUse); } } } private class SQLCursorData { PreparedStatement statement; String sqlSentence; boolean inUse; SQLCursorData (String sqlSentence, PreparedStatement statement) { this.sqlSentence = sqlSentence; this.statement = statement; this.inUse = true; } String getSQLSentence() { return sqlSentence; } PreparedStatement getPreparedStatement() { inUse = true; return statement; } boolean isInUse() { return inUse; } void setNotInUse () { inUse = false; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy