Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Jak odzyskać dane mysql za pomocą wielowątkowości Java?

package twcore.core.sql;

import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;

import twcore.core.BotSettings;
import twcore.core.SubspaceBot;
import twcore.core.events.;
import twcore.core.util.Tools;


/**
 * Thread-based main class for the core's SQL database functionality.
 * Initializes and manages SQL connection pools and queries, and runs
 * background queries on a semi-regular basis.
 * <p>
 *
 * Choosing a standard query vs. background/high-priority background query:
 * <p>
 * <b>Standard / foreground</b>    -  Runs exactly when needed.  Does not wait in
 * a queue to execute (unless connections are low).  Does not require a unique
 * identifier or special event handling.  However, a standard query will
 * pause the program thread until the results are returned.  For large queries
 * and bad connections this may result in long delays and unresponsiveness.
 * <p>
 * <b>Background</b>               -  Runs as a separate program thread.  Waits in a
 * queue and can be delayed by other queries waiting to execute.  Requires the
 * bot to catch an SQLResultEvent and use a unique identifier to refer to the
 * query.  As a separate thread, after the background query is run, the bot
 * continues execution as normal, causing no delays.  Ideal when multiple users
 * may need to access large amounts of SQL data from the same bot at the same
 * time without compromising responsiveness to the bot for others.  However,
 * their individual result sets may return more slowly than with a standard query.
 * <p>
 * <b>High-priority background</b> -  Same as a background query, but added to the
 * head of the queue.  Combines the versatility of a background queue with the
 * foreground's ability to return the result set almost instantly.
 * <p>
 * <b><u>IMPORTANT NOTE</b></u>
 * For every query you MUST run BotAction's SQLClose(), or manually run the close()
 * method on both the ResultSet and the Statement that created it.  If you do not,
 * memory leaks may occur!
 * 
 * TODO:
 * Setup Apache Commons DBCP to remove CommunicatonsExceptions:
 *    validationQuery="SELECT 1"
 *    testOnBorrow="true"
 */
public class SQLManager extends Thread {
    BotSettings sqlcfg;                            // Reference to SQL config file
    HashMap     <String,SQLConnectionPool>pools;   // Connection pool storage
    HashMap     <String,SQLBackgroundQueue>queues; // Background queue storage
    boolean     operational = true;                // Status of SQL system

    final static int THREAD_SLEEP_TIME = 30 * Tools.TimeInMillis.SECOND;
                                                   // Length of time for thread to
                                                   // sleep, in ms, after all
                                                   // background queries are done.

    final static int STALE_TIME = 15 * Tools.TimeInMillis.MINUTE;
                                                   // Time in ms between stale conn checks.
    private long nextStaleCheck = 0;

    /**
     * Initialize SQL functionality with the information given in the specified
     * configuration file.
     * @param configFile Properly formatted CFG file containing SQL system data
     */
    public SQLManager( File configFile ) {
        super("SQLManager");
        pools = new HashMap<String,SQLConnectionPool>();
        queues = new HashMap<String,SQLBackgroundQueue>();
        sqlcfg = new BotSettings( configFile );
        System.out.println( "=== SQL Initialization ===" );
        try{
            for( int i = 1; i <= sqlcfg.getInt( "ConnectionCount" ); i++ ){
                String name = sqlcfg.getString( "Name" + i );

                // TODO: Migrate to a DataSource object and pass that to SQLConnectionPool
                // (com.mysql.jdbc.jdbc2.optional.MysqlDataSource)
                String dburl = "jdbc:mysql://" + sqlcfg.getString( "Server" + i )
                + ":" + sqlcfg.getInt( "Port" + i ) + "/"
                + sqlcfg.getString( "Database" + i ) + "?user="
                + sqlcfg.getString( "Login" + i ) + "&password="
                + sqlcfg.getString( "Password" + i ) +

                // Available properties (and info about them)
                // http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-configuration-properties.html
                "&allowMultiQueries=true" +
                "&maxReconnects=2147483647" +
                "&initialTimeout=1" +
                "&logSlowQueries=false" +
                "&interactiveClient=true" +
                "&autoReconnect=true" +         // Auto-Reconnect not recommended
                "&autoReconnectForPools=true";

                // TODO: Better pooling solutions now exist that can be configured to our needs.
                SQLConnectionPool db = new SQLConnectionPool( name, dburl,
                sqlcfg.getInt( "MinPoolSize" + i ),
                sqlcfg.getInt( "MaxPoolSize" + i ),
                sqlcfg.getInt( "WaitIfBusy" + i ),
                sqlcfg.getString( "Driver" + i )
                );
                pools.put( name, db );
                queues.put( name, new SQLBackgroundQueue() );
            }
            Tools.printLog( "SQL Connection Pools initialized successfully." );
            for( Iterator<SQLConnectionPool> i = pools.values().iterator(); i.hasNext(); ){
                Tools.printLog( i.next().toString() );
            }
        } catch( SQLException e ){
            Tools.printLog( "Failed to load SQL Connection Pools.  Driver missing?" );
            operational = false;
            Tools.printLog( e.getMessage() );
        }
        if( operational ){
            start();
            Tools.printLog( "SQL Background Queues initialized." );
        } else {
            Tools.printLog( "SQL Background Queues NOT initialized." );
        }
        System.out.println();
        nextStaleCheck = System.currentTimeMillis() + STALE_TIME;
    }

    /**
     * Adds a regular background query to the end of the queue.  If there are no
     * queued queries ahead of it, the background query will be executed nearly
     * as quickly as a regularly executed query, but without delaying the bot's
     * thread to retrieve the result set.  The query is instead run in a new
     * thread and returned to the bot via an SQLResultEvent, and is identified by
     * a unique key (<CODE>identifier</CODE>).
     * @param connName Name of the connection as defined in sql.cfg
     * @param identifier The unique identifier for this query
     * @param query A properly-formed SQL query
     * @param bot The bot requesting the query (if unsure, use <b>this</b>)
     */
    public void queryBackground( String connName, String identifier,
    String query, SubspaceBot bot ){
        if( !operational ){
            Tools.printLog( "Unable to process query: " + query );
        } else {
            if( !pools.containsKey( connName )) {
                Tools.printLog( "Invalid connection name supplied: '" + connName + "'" );
                return;
            }
            SQLBackgroundQueue queue = queues.get( connName );
            queue.addQuery( new SQLResultEvent( query, identifier, bot ));
            interrupt();
        }
    }

    /**
     * Adds a background query to the front of the queue.  A high-priority
     * background query will be executed nearly as quickly as a regularly
     * executed query, but without delaying the bot's thread to retrieve the
     * results.  The query is instead run in a new thread and returned to the bot
     * via an SQLResultEvent, and is identified by a unique key (<CODE>identifier</CODE>).
     * @param connName Name of the connection as defined in sql.cfg
     * @param identifier The unique identifier for this query
     * @param query A properly-formed SQL query
     * @param bot The bot requesting the query (if unsure, use <b>this</b>)
     */
    public void queryBackgroundHighPriority( String connName, String identifier,
    String query, SubspaceBot bot ){
        if( !operational ){
            Tools.printLog( "Unable to process background high priority query: " + query );
        } else {
            if( !pools.containsKey( connName )) {
                Tools.printLog( "Invalid connection name supplied: '" + connName + "'" );
                return;
            }
            SQLBackgroundQueue queue = queues.get( connName );
            queue.addHighPriority( new SQLResultEvent( query, identifier, bot ));
            interrupt();
        }
    }

    /**
     * Runs a regular SQL query using the specified database connection.  Your
     * bot's thread will not continue while the query is in effect.  Use a
     * background query if you wish for the thread to continue while the query
     * is executed.
     * @param connectionName Name of the connection as defined in sql.cfg
     * @param query A properly-formed SQL query
     * @return The result set of the query (MAY be null)
     * @throws SQLException
     */
    public ResultSet query( String connectionName, String query ) throws SQLException {
        if( !operational ){
            Tools.printLog( "Unable to process query: " + query );
            return null;
        } else {
            if( !pools.containsKey( connectionName )) {
                Tools.printLog( "Invalid connection name supplied: '" + connectionName + "'");
                return null;
            }
            return pools.get( connectionName ).query( query );
        }
    }


    /**
     * Creates a PreparedStatement.
     * Gets a Connection from the specified SQLConnectionPool (specified by the connectionName)
     * and creates a PreparedStatement object using the specified query.
     * Note that this sets the connection to "busy" in the SQLConnectionPool so it isn't used by other processes.
     *
     * You need to free it when the bot doesn't use the PreparedStatement anymore or this will be a Connection-leak !!
     *
     * @param connectionName Name of the connection as defined in sql.cfg
     * @param uniqueID A unique string that is used for re-using (busy) Connections in the connection pool. This is only used for PreparedStatements as their Connection is locked when a bot creates a PreparedStatement.
     * @param sqlstatement The (dynamic) SQL INSERT/UPDATE statement that will be pre-parsed for the PreparedStatement
     * @param retrieveAutoGeneratedKeys whether auto-generated keys should be returned
     * @return PreparedStatement object or null if there was an error
     */
    public PreparedStatement createPreparedStatement(String connectionName, String uniqueID, String sqlstatement, boolean retrieveAutoGeneratedKeys) {
        if( !operational ) {
            Tools.printLog( "Unable to create PreparedStatement object; SQL System is not operational");
            return null;
        } else {
            if(!pools.containsKey( connectionName ))
                return null;
            else {
                try {
                    // Have we hit the maximum number of allowed connections in the pool?
                    if(pools.get(connectionName).isAvailable() || pools.get(connectionName).totalConnections() < pools.get(connectionName).getMaxConnections()) {
                        Connection conn = pools.get( connectionName ).getConnection(uniqueID);
                        if(retrieveAutoGeneratedKeys)
                            return conn.prepareStatement(sqlstatement, Statement.RETURN_GENERATED_KEYS);
                        else
                            return conn.prepareStatement(sqlstatement, Statement.NO_GENERATED_KEYS);
                    } else {
                        Tools.printLog("No more connections available in pool '"+connectionName+"' to create PreparedStatement!");
                        return null;
                    }
                } catch(SQLException sqle) {
                    Tools.printLog("SQLException encountered while trying to create a PreparedStatement from a Connection from '"+connectionName+"':"+sqle.getMessage());
                    return null;
                }
            }
        }
    }

    /**
     * Frees specified Connection for specified connectionpool using specified unique ID.
     * This should be used when closing a PreparedState\ment as it locks a connection on creation.
     *
     * @param connectionName Name of the connection as defined in sql.cfg
     * @param uniqueID The unique ID used to create the Prepared Statement
     * @param conn Connection used when creating a PreparedStatement
     */
    public void freeConnection(String connectionName, String uniqueID, Connection conn) {
        if( !operational ) {
            Tools.printLog( "Unable to free Connection; SQL System is not operational");
        } else {
            if(pools.containsKey( connectionName )) {
                pools.get( connectionName ).free(uniqueID, conn);
            }
        }
    }

    /**
     * @return True if the SQL system is operational
     */
    public boolean isOperational(){
        return operational;
    }



    /**
     * Prints to the log file the status of all connection pools.
     */
    public void printStatusToLog(){
        if( !operational ){
            Tools.printLog( "SQL Connection Not Operational" );
        } else {
            Tools.spamLog( getPoolStatus() );
        }
    }

    /**
     * Gets status of all connection pools.
     * @return String array containing status of each individual connection pool.
     */
    public String[] getPoolStatus() {
        String[] status = new String[pools.size()];
        Iterator<SQLConnectionPool> i = pools.values().iterator();
        for(int j = 0; j<status.length; j++)
            status[j] = i.next().toString();
        return status;
    }

    /**
     * Checks the background queue for queries waiting to be run, and dispatches
     * them each to a separate SQLWorker thread.  After the result is received,
     * it's then returned as an SQLResultEvent to the bot that made the query.
     * The SQLManager thread will sleep for a defined amount of time, but will
     * interrupt/return when a background query requires processing.
     * 
     * Also runs a check for stales on each pool of connections periodically.
     */
    public void run() {
        boolean checkForStales;
        while( true ){

            // Run background queries
            Iterator<String> i = queues.keySet().iterator();
            while( i.hasNext() ){
                String name = i.next();
                SQLBackgroundQueue queue = queues.get( name );
                SQLConnectionPool pool = pools.get( name );
                while( !queue.isEmpty() && !pool.reachedMaxBackground() ){
                    SQLResultEvent event = queue.getNextInLine();
                    try {
                        new SQLWorker( pool, event, this );
                    } catch (Exception e) {
                        Tools.printLog("Uncaught exception encountered running background query.");
                        Tools.printStackTrace(e);
                    }                
                }
            }

            // Perform stale check
            checkForStales = (nextStaleCheck < System.currentTimeMillis());            
            i = pools.keySet().iterator();
            while( i.hasNext() ) {
                String name = i.next();
                SQLConnectionPool pool = pools.get( name );
                if( checkForStales )
                    pool.updateStaleConnections();
            }            
            if( checkForStales )
                nextStaleCheck = System.currentTimeMillis() + STALE_TIME;
            try{
                Thread.sleep( THREAD_SLEEP_TIME );
            } catch( InterruptedException e ){}
        }
    }

}
package twcore.core.sql;

import java.sql.ResultSet;
import java.sql.SQLException;

import twcore.core.events.SQLResultEvent;
import twcore.core.util.Tools;

/**
 * Runs a background SQL query given a connection pool to use and an undelivered
 * SQLResultEvent object to place the results into.  By handling in a separate
 * thread, it frees the bot process of having to wait on a query. 
 */
public class SQLWorker implements Runnable {
    private SQLResultEvent    m_event;      // Event to hand the ResultSet to     
    private SQLConnectionPool m_pool;       // Connection pool to run query on
    private SQLManager        m_manager;    // For interrupting any waits

    /**
     * Creates a new SQLWorker and begins a background query, given a connection
     * pool to use for the query, an event to place the result set returned by
     * the query into, and an SQLManager to wake up/interrupt when the process
     * has finished (if it is currently sleeping).
     * @param pool Connection pool to use to run the query
     * @param event Event that will afterward contain the returned ResultSet
     * @param manager Waiting object to interrupt when finished
     */
    public SQLWorker( SQLConnectionPool pool, SQLResultEvent event, SQLManager manager ) {
        m_pool = pool;
        m_manager = manager;
        Thread t = new Thread( this, "SQLWorker" );
        m_event = event;
        m_pool.incrementBackgroundCount();
        t.start();
    }

    /**
     * Runs the SQL query found in the SQLResultEvent the SQLWorker was instantiated
     * with.  Sets the returned ResultSet inside the event, which in turn will fire
     * the event in the bot so as to be handled and fetched by unique key.  After
     * this is done, the background queue count of the connection pool used is
     * reduced by one, and the SQLManager that called the worker is interrupted
     * back into consciousness, if it is currently asleep.   
     */
    public void run() {
        try{            
            ResultSet set = m_pool.query( m_event.getQuery() );
            m_event.setResultSet( set );
            m_pool.decrementBackgroundCount();
            m_manager.interrupt();
        } catch( SQLException e ){
            Tools.printLog("SQLException encountered while running background query in SQLWorker.");
            Tools.printStackTrace( e );
        }
    }
}

Jest więcej plików, ale nie mogę ich wkleić, ponieważ osiągnąłem limit w tym pytaniu. Po prostu skieruję Cię za pomocą adresu URL

http://www.twcore.org/browser/trunk /twcore/src/twcore/core/sql




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Zapytanie SQL, aby uzyskać sumę częściową niektórych wierszy

  2. struktura mysql dla postów i komentarzy

  3. Automatyczne zwiększanie wartości niestandardowych MySQL

  4. BŁĄD 1054 (42S22):Nieznana kolumna „‍‍” w „liście pól”

  5. Nie mogę wyeksportować mojej bazy danych z mysql workbench