Transacción JAVA, preguntas de la entrevista de transacción, transacción mysql, transacción jdbc, transacción DButils, transacción de uso para completar la función de transferencia.

1. Las características y el nivel de aislamiento de la transacción (preguntas de la entrevista)

  1. Características de la transacción ACID
  • Atomicidad: se refiere a que la transacción es una unidad de trabajo indivisible, operaciones en la transacción. O todo sucede o no sucede.
  • Coherencia: en una transacción, la integridad de los datos antes y después de la transacción debe ser coherente.
  • Aislamiento: cuando varios usuarios acceden a la base de datos al mismo tiempo, la transacción de un usuario no puede ser interferida por las transacciones de otros usuarios, y los datos entre múltiples transacciones simultáneas deben aislarse entre sí.
  • Durabilidad: una vez que se confirma una transacción, sus cambios en los datos de la base de datos son permanentes y no deberían tener ningún impacto incluso si la base de datos falla.
  1. Problemas de acceso concurrente: causados ​​por aislamiento
    Si no se considera el aislamiento , hay tres problemas de acceso concurrente en las transacciones.
  • Lectura sucia: la transacción B ha leído datos que aún no han sido comprometidos por la transacción A ------ La transacción B es necesaria para leer los datos enviados por la transacción A
  • Lectura no repetible: el contenido de los datos leídos dos veces en una transacción es inconsistente; el requisito es que los datos sean consistentes cuando se leen varias veces en una transacción.
  • Lectura fantasma / lectura virtual: la cantidad de datos leídos dos veces en una transacción es inconsistente ----- La cantidad de datos leídos varias veces en una transacción debe ser la misma: insertar eliminar

3. El nivel de aislamiento de la transacción

  • read uncommitted : Leer datos que no se han enviado: No se puede resolver ningún problema
  • read committed: Leer los datos enviados: Las lecturas sucias se pueden resolver ---- Oracle predeterminado
  • repeatable read: Releer lectura: Las lecturas sucias y las lecturas no repetibles se pueden resolver — mysql predeterminado
  • serializable: Serialización: puede resolver lectura sucia, lectura no repetible y lectura virtual equivalente a la tabla de bloqueo

Niveles de aislamiento de rendimiento :
read uncommitted> read committed> repeatable read> serialazable
Seguridad:
read uncommitted < read committed< repeatable read<serialazable

Dos, transacción MySQL

1. Qué es una transacción

Una cosa tiene n unidades constituyentes o estas n unidades constituyentes tienen éxito al mismo tiempo o n unidades fallan al mismo tiempo. Es poner n componentes en una transacción

2. La
transacción predeterminada de la transacción mysql :

Una declaración SQL es una transacción por defecto para abrir la transacción y confirmar la transacción.

Transacción manual :

  • Abrir una transacción mostrada: iniciar transacción
  • Envío de la transacción: el compromiso representa que todo el sql desde el inicio de la transacción hasta el envío de la transacción se considera efectivo y realmente actualiza la base de datos. Si olvida escribir el compromiso, la transacción se revertirá de forma predeterminada
  • Reversión de la transacción: la reversión representa la reversión de la transacción. Todas las operaciones SQL desde la apertura de la transacción hasta la reversión de la transacción se consideran inválidas y la base de datos no se ha actualizado.

Caso:
1. Primero creamos una base de datos, la tabla se llama cuenta

START TRANSACTION;
INSERT INTO account VALUES(NULL,'jack',5000);
COMMIT;

Se ejecuta la declaración sql anterior y la transacción se ejecuta correctamente.

2. Si no escribimos una confirmación

START TRANSACTION;
INSERT INTO account VALUES(NULL,'lucy',5000);

Luego, puede consultar los datos en la ventana cmd, pero los datos no existen en el disco pero se imprimen en el archivo de registro.

3. La reversión de la transacción

START TRANSACTION;
INSERT INTO account VALUES(NULL,'amy',5000);
ROLLBACK;

La instrucción sql no se puede insertar porque la transacción se revierte

Tres, operaciones de transacción JDBC

El valor predeterminado es la transacción automática :

Ejecute la instrucción sql: executeUpdate () ---- Cada vez que se ejecuta el método executeUpdate, la transacción se envía automáticamente.

Transacción manual a través de la API de jdbc :

Abra la transacción: conn.setAutoComnmit (false);
Confirme la transacción: conn.commit ();
Revertir la transacción: conn.rollback ();
Nota: La conexión de la transacción de control debe ser la misma
La conexión que ejecuta sql y la conexión que inicia la transacción debe ser la misma para controlar la transacción.

//通过jdbc去控制事务
		Connection connection=null;
		//1.注册驱动
		try {
    
    Class.forName("com.mysql.jdbc.Driver");
		//2.获取连接
		connection= DriverManager.getConnection("jdbc:mysql:///web19","root","123");
		//手动开启事务
		connection.setAutoCommit(false);
		//3.获取执行平台
		Statement statement =connection.createStatement();
		//4.操作sql
		statement.executeUpdate("insert into account values(null,'zhangsan',6000)");
		
		//statement.executeUpdate("insert into account values(null,'lisi',7000)");
		
		//提交事务
		connection.commit();
		//关闭
		statement.close();
		connection.close();
		}catch (Exception e) {
    
    
			// TODO: handle exception
			try {
    
    
				connection.rollback();
			} catch (SQLException e1) {
    
    
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}

Cuatro, operación de transacción DBUtils

QueryRunner
Estructura paramétrica: QueryRunner runner = new QueryRunner (DataSource dataSource);
una estructura parametrizada pasa la fuente de datos (grupo de conexiones) como un parámetro a QueryRunner, QueryRunner obtendrá un recurso de conexión de la base de datos del grupo de conexiones para operar la base de datos, así que use directamente el método de actualización sin el parámetro Connection Puede operar la base de datos

Sin estructura de parámetros: QueryRunner runner = new QueryRunner ();
La construcción sin parámetros no pasa la fuente de datos (pool de conexiones) como parámetro a QueryRunner, entonces debemos usar el método con el parámetro Connection cuando usamos el objeto QueryRunner para operar la base de datos

En pocas palabras, cuando nuestro sql necesita control de transacciones, usa una estructura sin parámetros y no requiere control de transacciones para usar una estructura parametrizada.

QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
		Connection conn=null;
		try {
    
    
			//获得一个Connection
			conn =DataSourceUtils.getConnection();
			//开启事务
			conn.setAutoCommit(false);
			//runner.update("update account set money=1000 where name='tom");
			runner.update(conn, "update account set money=1000 where name='tom");
			//提交或回滚事务
			conn.commit();
		} catch (SQLException e) {
    
    
			try {
    
    
				conn.rollback();
			} catch (SQLException e1) {
    
    
				
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}

Cinco, completa un caso de función de transferencia

Primero analice la función de transferencia web en modo MVC
Inserte la descripción de la imagen aquí

El primer paso: escribe un jsp primero

<form action="${pageContext.request.contextPath}/transfer" method="post">
			转出账户:<input type="text" name="out"><br/>
			转入账户:<input type="text" name="in" ><br/>
			转账金额:<input type="text" name="money"><br/>
			<input type="submit" value="确认转账"><br/>
</form>

Paso 2: Escribe la capa web, responsable de obtener los datos de la página, llama a los datos de la capa de servicio, la
capa web es un servlet, necesitas construir un servlet tú mismo

@WebServlet("/transfer")
public class TransferServlet extends HttpServlet {
    
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
		//接受转账的参数
		String out = request.getParameter("out");
		String in =request.getParameter("in");
		String moneyStr =request.getParameter("money");
		double money =Double.parseDouble(moneyStr);
		//调用业务层的转账方法
		TransferService service =new TransferService();
		boolean isTransferSuccess= service.tranfer(out,in,money);
		response.setContentType("text/html;charset=UTF-8");
		if (isTransferSuccess) {
    
    
			response.getWriter().write("转账成功!!!");
		}else {
    
    
			response.getWriter().write("转账失败!!");
		}
	}

	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

Paso 3:
escriba la capa dao, procese los datos en la base de datos y retroalimente a la capa de servicio

public void out(Connection conn,String out, double money) throws SQLException {
    
    
		QueryRunner queryRunner =new QueryRunner();
		//Connection conn =DataSourceUtils.getConnection();
		String sql ="update account set money =money-? where name=?";
		queryRunner.update(conn,sql,money,out);
		
	}

	public void in(Connection conn,String in, double money) throws SQLException {
    
    
		QueryRunner queryRunner =new QueryRunner();
		//Connection conn =DataSourceUtils.getConnection();
		String sql ="update account set money =money+? where name=?";
		queryRunner.update(conn,sql,money,in);
		
	}

Paso 4:
escribe la capa de servicio

public boolean tranfer(String out, String in, double money) {
    
    
		//创建dao对象
		TransferDao dao =new TransferDao();
		
		boolean isTranferSuccess =true;
		Connection conn=null;
		try {
    
    
			conn =DataSourceUtils.getConnection();
			conn.setAutoCommit(false);
			
			//转出方法
			dao.out(conn,out,money);
			//转入方法
			dao.in(conn,in,money);
		} catch (Exception e) {
    
    		
			isTranferSuccess =false;	
			//回滚事务
			try {
    
    
				conn.rollback();
			} catch (SQLException e1) {
    
    
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally {
    
    
			try {
    
    
				conn.commit();
			} catch (SQLException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
		return isTranferSuccess;
	}

Sexta versión mejorada: use ThreadLocal para vincular recursos de conexión

Debido a que queremos usar transacciones, no se ajusta al modelo MVC. En nuestro código anterior, Connection se usa en la capa de Servicio. Pero Connection, aparece en la base de datos. Debe usarse en la capa dao. Por tanto, deberíamos recomendar el método ThreadLocal.
El método ThreadLocal utiliza el conocimiento de los hilos, transferiremos esta línea y la completaremos en un hilo.

El primer paso:
herramientas de escritura

public class MyDataSourceUtils {
    
    
	//获取Connection------从连接池中获取
	private static ComboPooledDataSource dataSource =new ComboPooledDataSource();	
	//创建ThreadLocal-------相当于一个map集合
	private static ThreadLocal<Connection> tl =new ThreadLocal<Connection>();
	
	//开启事务
	public static void startTransaction() throws SQLException {
    
    
		Connection connection= getCurrentConnection();
		connection.setAutoCommit(false);
	}
	
	//获得当前线程上绑定的conn
	public static Connection getCurrentConnection() throws SQLException {
    
    
		//从ThreadLocal寻找当前线程是否有对应Connection
		Connection conn =tl.get();
		if (conn ==null) {
    
    
			//获得新的connection
			conn=getConnection();
			//将conn资源绑定到ThreadLocal(map)上
			tl.set(conn);
		}
		return conn;
	}
	
	public static Connection getConnection() throws SQLException {
    
    
		return dataSource.getConnection();
	}
	
	//事务回滚
	public static void rollback() throws SQLException {
    
    
		getCurrentConnection().rollback();
	}
	//事务提交
	public static void commit() throws SQLException {
    
    
		getCurrentConnection().commit();
		//将Connection从ThreadLocal中移除
		tl.remove();
		getCurrentConnection().close();
	}
}

Paso 2:
modificar la capa de servicio

public class TransferService {
    
    

	public boolean tranfer(String out, String in, double money) {
    
    
		//创建dao对象
		TransferDao dao =new TransferDao();
		
		boolean isTranferSuccess =true;
		Connection conn=null;
		try {
    
    
			//开启事务
			//conn =DataSourceUtils.getConnection();
			//conn.setAutoCommit(false);
			//使用ThreadLocal存储Connection
			MyDataSourceUtils.startTransaction();
			
			//转出方法
			dao.out(out,money);
			//转入方法
			//int i=1/0;
			dao.in(in,money);
		} catch (Exception e) {
    
    
			
			isTranferSuccess =false;	
			//回滚事务
			try {
    
    
				MyDataSourceUtils.rollback();
			} catch (SQLException e1) {
    
    
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally {
    
    
			try {
    
    
				MyDataSourceUtils.commit();
			} catch (SQLException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
		return isTranferSuccess;
	}

}

Paso 3:
modifica la capa dao

public class TransferDao {
    
    

	public void out(String out, double money) throws SQLException {
    
    
		QueryRunner queryRunner =new QueryRunner();
		Connection conn =MyDataSourceUtils.getCurrentConnection();
		String sql ="update account set money =money-? where name=?";
		queryRunner.update(conn,sql,money,out);
		
	}

	public void in(String in, double money) throws SQLException {
    
    
		QueryRunner queryRunner =new QueryRunner();
		Connection conn =MyDataSourceUtils.getCurrentConnection();
		String sql ="update account set money =money+? where name=?";
		queryRunner.update(conn,sql,money,in);
		
	}

}

Tenga en cuenta que ThreadLocal es una colección de mapas. Su clave es principalmente el nombre del hilo, y el valor solo debe colocarse en nuestra conexión.

Supongo que te gusta

Origin blog.csdn.net/Mr_GYF/article/details/109125561
Recomendado
Clasificación