数据库开发(十二 JDBC篇四)批处理(了解),事务(transaction)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhang___gang/article/details/84645373

批处理

添加10000个用户到用户表

批处理:一次发送多个数据给服务器,服务器一个一个的执行,提高效率

  • addBatch():把语句,参数添加到PreparedStatement中
  • executeBatch():一次把所有数据发送给服务器,由服务器一条一条的执行

示例展示

package com.zhang.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import com.zhang.entity.User;
import com.zhang.util.ConnectionFactory;
import com.zhang.util.DBUtils;

/*
 * 批处理演示
 */
public class AddUsers {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		AddUsers addUsers = new AddUsers();
		try {
			//addUsers.addUser();
			addUsers.addUser1();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	/**
	 * 常规方法演示
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 */
	public void addUser() throws ClassNotFoundException, SQLException {
		long t1 = System.currentTimeMillis();
		Connection conn = ConnectionFactory.getConnection();
		
		//第三步:创建可以执行SQL语句的PreparedStatement
		String sql = "INSERT INTO t_user(id,username,password,sex,id_number,tel,addr)" 
				+ "VALUES(t_user_id_seq.NEXTVAL,?,?,?,?,?,?)";
		PreparedStatement pstmt =conn.prepareStatement(sql);
		//第四步:执行SQL语句
		//先设置占位符的值
		for(int i=0; i < 10000; i++) {
			pstmt.setString(1, "aa" + i);
			pstmt.setString(2, "111");
			pstmt.setInt(3, 0);
			pstmt.setString(4, "321" + i);
			pstmt.setString(5, "1");
			pstmt.setString(6, "1");
			int rows = pstmt.executeUpdate();
		}
		//执行	
		DBUtils.close(pstmt,conn);
		long t2 = System.currentTimeMillis();
		System.out.println("共花费" +(t2-t1) + "毫秒");
	}
	
	/**
	 * 批处理演示
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 */
	public void addUser1() throws ClassNotFoundException, SQLException {
		long t1 = System.currentTimeMillis();
		Connection conn = ConnectionFactory.getConnection();
		
		//第三步:创建可以执行SQL语句的PreparedStatement
		String sql = "INSERT INTO t_user(id,username,password,sex,id_number,tel,addr)" 
				+ "VALUES(t_user_id_seq.NEXTVAL,?,?,?,?,?,?)";
		PreparedStatement pstmt =conn.prepareStatement(sql);
		//第四步:执行SQL语句
		//先设置占位符的值
		for(int i=0; i < 10000; i++) {
			pstmt.setString(1, "bb" + i);
			pstmt.setString(2, "111");
			pstmt.setInt(3, 0);
			pstmt.setString(4, "322" + i);
			pstmt.setString(5, "1");
			pstmt.setString(6, "1");
			pstmt.addBatch();
		}
		//执行
		//一次发送10000套数据过去,降低Java与数据库的交互次数
		pstmt.executeBatch();
		DBUtils.close(pstmt,conn);
		long t2 = System.currentTimeMillis();
		System.out.println("共花费" +(t2-t1) + "毫秒");
	}

}

事务

  • 可以保证多个操作要么都成功要么都失败
  • 在Java中默认所有的操作都是自动提交
  • Java中什么时候考虑使用事务:一个业务如果涉及多个DML操作,可以使用事务控制

下单

  • 生成订单
  • 生成明细
  • 1. 生成订单和生成订单明细在同一事务中,需要使用同一个连接,Connection
  • 2. 但凡有一个出问题,就回滚,如果两个都成功,则提交、
  • 3. 提交模式改成手动提交,Java默认自动提交

Java事务控制步骤:

try{
    建立连接;
    设置为手动提交模式;
    操作1;
    操作2;
    提交;
}catch() {
    但凡有问题则回滚;
}finally{
    释放连接;
}

事务相关的方法:在Connection中

  • conn.setAutoCommit(false); --手动提交
  • conn.commit(); --提交
  • conn.rollback();   --回滚
  • 注:这些针对于当前这个Connection对象

利用上一篇中的练习2进行修改演示,结果如下:

User,Order,OrderDetail和UserJDBC四个类不做修改

首先在前面封装的工具类DBUtils类中加入两个方法,如下

package com.zhang.util;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 资源的释放
 * @author zygycp
 *
 */

public class DBUtils {
	
	public static void close(ResultSet rs, Statement stmt,Connection conn) {
		if(rs!=null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(stmt!=null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(conn!=null) {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	//方法重载
	public static void close(Statement stmt,Connection conn) {
		close(null,stmt,conn);
	}
	
	public static void close(Statement stmt) {
		close(null,stmt,null);
	}
	
	public static void close(Connection conn) {
		close(null,null,conn);
	}

}

OrderJDBC类:

package com.zhang.jdbc.copy.transaction;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.zhang.entity.Order;
import com.zhang.util.ConnectionFactory;
import com.zhang.util.DBUtils;

public class OrderJDBC {
	
	/**
	 * 生成订单号
	 * @return 订单号
	 * @throws SQLException
	 */
	public long generateOrderId() throws SQLException {
		long orderId = 0;
		Connection conn = ConnectionFactory.getConnection();
		//此序列在sql数据库文件中已经定义
		String sql = "SELECT t_order_id_seq.NEXTVAL FROM dual";
		PreparedStatement pstmt = conn.prepareStatement(sql);
		ResultSet rs = pstmt.executeQuery();
		if(rs.next()) {
			orderId = rs.getLong(1);
		}
		DBUtils.close(rs,pstmt, conn);
		return orderId;
	}
	
	
	/**
	 * 添加订单
	 * @param order:订单信息
	 * @return 1:添加成功   0:添加失败
	 * @throws SQLException
	 */
	public int addOrder(Order order,Connection conn) throws SQLException {
		String sql = "INSERT INTO t_order (id,user_id,total,order_date,"
				+ "name,tel,addr,status) VALUES(?,?,?,sysdate,?,?,?,?)";
		PreparedStatement pstmt = conn.prepareStatement(sql);
		pstmt.setLong(1, order.getId());
		pstmt.setLong(2, order.getUserId());
		pstmt.setDouble(3, order.getTotal());
		pstmt.setString(4, order.getName());
		pstmt.setString(5, order.getTel());
		pstmt.setString(6, order.getAddr());
		pstmt.setInt(7, order.getStatus());
		int rows = pstmt.executeUpdate();
		DBUtils.close(pstmt);
		return rows;
		
	}
}

OrderDetailJDBC类:

package com.zhang.jdbc.copy.transaction;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import com.zhang.entity.OrderDetail;
import com.zhang.util.ConnectionFactory;
import com.zhang.util.DBUtils;

public class OrderDetailJDBC {
	public int addOrderDetail(OrderDetail detail,Connection conn) throws SQLException {
		String sql = "INSERT INTO t_order_detail(id,order_id,product_id,price,quantity,cost)"
			+ "VALUES(t_order_detail_id_seq.NEXTVAL,?,?,?,?,?)";
		PreparedStatement pstmt = conn.prepareStatement(sql);
		pstmt.setLong(1, detail.getOrderId());
		pstmt.setLong(2, detail.getProductId());
		pstmt.setDouble(3, detail.getPrice());
		pstmt.setInt(4, detail.getQuantity());
		pstmt.setDouble(5, detail.getCost());
		int rows = pstmt.executeUpdate();
		DBUtils.close(pstmt);
		return rows;
	}
}

TestOrder类:

package com.zhang.jdbc.copy.transaction;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.zhang.entity.Order;
import com.zhang.entity.OrderDetail;
import com.zhang.util.ConnectionFactory;
import com.zhang.util.DBUtils;

public class TestOrder {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TestOrder to = new TestOrder();
		Order order = new Order(0,5,19,null,"xiaoming","111","111",0);
		List<OrderDetail> list = new ArrayList<>();
		list.add(new OrderDetail(0,0,2,15,1,15));
		list.add(new OrderDetail(0,0,4,2,3,6));
		System.out.println(to.takeOrder(order, list));

	}
	
	//一个下单业务,涉及多个操作,列用事务控制
	public boolean takeOrder(Order order, List<OrderDetail> list) {
		
		OrderJDBC orderJDBC = new OrderJDBC();
		OrderDetailJDBC detailJDBC = new OrderDetailJDBC();
		Connection conn = null;
		try {
			//完成下单业务
			//1.生成订单编号
			long orderId = orderJDBC.generateOrderId();
			//先获取连接
			conn = ConnectionFactory.getConnection();
			//设置为手动提交模式
			conn.setAutoCommit(false);
			
			//2.生成订单
			//把订单号赋给order
			order.setId(orderId);
			orderJDBC.addOrder(order,conn);
		    //3.生成订单明细
			for(int i = 0; i < list.size();i++) {
				list.get(i).setOrderId(orderId);
				detailJDBC.addOrderDetail(list.get(i),conn);
			}
			//都成功,提交
			conn.commit();
			return true;
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			try {
				//如果有问题,则回滚
				conn.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			return false;
		}
		finally {
			DBUtils.close(conn);
		}
	    
	}

}

事务并发引发的问题:

  • 脏读(dirty read):一个事务读到了另一事务为提交的更新数据,
    • 应该要避免
  • 不可重复读(UNrepeatable read):一个事务期间两次读取同一个数据单内容不一致,原因是两次读取期间有其它事务对数据作了更新并提交。可理解为一个事务读到另一个事务已经提交的更新数据。
    • 通常不需要避免的
  • 虚读/幻影读(phantom read):一个事务期间两次读取相同统计数据但内容不一致,原因是两次读取期间有其它事务执行了插入或删除符合统计条件的数据并提交。可理解为一个事务读到另一个事务已经提交的新插入或删除的数据。
    • 通常不需要避免的

事务隔离级别:

0:TRANSACTION_NONE(0)   不支持事务

1:TRANSACTION_READ_UNCOMMITTED(1)  脏读

2:TRANSACTION_READ_COMMITTED(2)  避免脏读

4:TRANSACTION_REPEATABLE_READ(4)  避免不可重复度

8:TRANSACTION_SERIALIZABLE(8)  避免虚读

事务隔离级别越高,并发性越差,一般设置为2

Oracle只支持2,8,默认是2(所以一般不用设置)

例:

conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

猜你喜欢

转载自blog.csdn.net/zhang___gang/article/details/84645373