package database.tools;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * Gestion des niveaux et des portées d'isolation 
 * des transactions utilisé pour les opérations 
 * sur les tables InnoDB.
 * @author Alfredo Toyo
 *
 */
public class IsolationLevel {
	private Connection connection;
	private Statement statement;
	private String query;
	private String isolationScope;
	private final String REPEATABLE_READ  = "REPEATABLE-READ";
	private final String READ_COMMITTED   = "READ-COMMITTED";
	private final String READ_UNCOMMITTED = "READ-UNCOMMITTED";
	private final String SERIALIZABLE     = "SERIALIZABLE";
	
	/**
	 * Constructeur
	 * @param connection
	 */
	public IsolationLevel(Connection connection){
		this.connection = connection;
		setSessionIsolationScope(); //Portée d'isolation SESSION par défaut
	}
	
	/**
	 * Repeatable read signifie "lecture répétable", c'est-à-dire que 
	 * si l'on fait plusieurs requêtes de sélection (non-verrouillantes) de suite, 
	 * elles donneront toujours le même résultat, quels que soient les changements 
	 * effectués par d'autres sessions.
	 * @throws SQLException
	 */
	public void setREPEATABLE_READ() throws SQLException{
		executeIsolationLevel(REPEATABLE_READ);
	}
	
	/**
	 * Chaque requête SELECT (non-verrouillante) va reprendre une "photo" (snapshot) 
	 * à jour de la base de données, même si plusieurs SELECT se font dans la même 
	 * transaction. Ainsi, un SELECT verra toujours les derniers changements commités, 
	 * même s'ils ont été faits dans une autre session, après le début de la transaction.
	 * @throws SQLException
	 */
	public void setREAD_COMMITTED() throws SQLException{
		executeIsolationLevel(READ_COMMITTED);
	}
	
	/**
	 * Le niveau READ UNCOMMITTED fonctionne comme READ COMMITTED, 
	 * si ce n'est qu'il autorise la "lecture sale". C'est-à-dire 
	 * qu'une session sera capable de lire des changements encore 
	 * non commités par d'autres sessions.
	 * @throws SQLException
	 */
	public void setREAD_UNCOMMITTED() throws SQLException{
		executeIsolationLevel(READ_UNCOMMITTED);
	}
	
	/**
	 * Ce niveau d'isolation se comporte comme REPEATABLE READ, 
	 * sauf que lorsque le mode autocommit est désactivé, tous 
	 * les SELECT simples sont implicitement convertis en 
	 * SELECT ... LOCK IN SHARE MODE.
	 * @throws SQLException
	 */
	public void setSERIALIZABLE() throws SQLException{
		executeIsolationLevel(SERIALIZABLE);
	}
	
	/**
	 * Le mot-clé GLOBAL définit le niveau de portée d'isolation 
	 * pour toutes les sessions MySQL qui seront créées dans le futur. 
	 * Les sessions existantes ne sont pas affectées.
	 */
	public void setGlobalIsolationScope(){
		isolationScope = "GLOBAL";
	}
	
	/**
	 * Le mot-clé SESSION définit le niveau de portée d'isolation 
	 * pour toutes les transactions ultérieures effectuées dans 
	 * la session en cours.
	 */
	public void setSessionIsolationScope(){
		isolationScope = "SESSION";
	}
	
	/**
	 * Cette méthode exécute l'instruction SQL pour définir le niveau 
	 * d'isolation des transactions qui est passé en paramètre.
	 * @param isolationLevel
	 * @throws SQLException
	 */
	private void executeIsolationLevel(String isolationLevel) throws SQLException{
		statement = connection.createStatement();
		query = "SET " + isolationScope +" tx_isolation = '"+ isolationLevel +"'";
		statement.execute(query);
	}
}
