元数据、事务及手动添加事务、利用缓存进行批处理数据、利用Connection的重载的prepareStatemen()方法获取表的某些字段的值。
package test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import org.junit.Test;
import dao.EmpDao;
import entity.Emp;
import util.DBUtil;
----------------------------------------------------------------------------------------
元数据(MetaData)
- 元:根本、本质
- 元数据:数据的本质,数据的描述/概述
- 用来描述数据的数据叫元数据
结果集元数据(ResultSetMetaData)
- 用来描述结果集的数据
- 如:返回的结果有几列、列名是什么、列的类型是什么
public class Test {
/**
* ResultSetMetData(了解)
*/
@Test
public void test(){
Connection con = null;
try {
con = DBUtil.getConnection();
String sql = "SELECT * FROM emp ";
PreparedStatement ps = con.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
/*
* 获取结果集元数据。
* 元数据中存储了描述该结果集的信息。
*/
ResultSetMetaData md = rs.getMetaData();
System.out.println(md.getColumnCount());//字段数
System.out.println(md.getColumnName(1));//第一列的字段名
System.out.println(md.getColumnClassName(1));//第一列的字段类型
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("查询员工失败!",e);
}finally{
DBUtil.close(con);
}
}
}
-----------------------------------------------------------------------------------------
事务:
什么叫事务?
满足如下特征的数据库访问叫事务:1. 原子性:事务是完整的,要么都成功,要么都失败。
2. 一致性:事务前后的数据要保持一致,即收支平衡。
3. 隔离性:事务过程中的数据不能被别人访问,需要受隔离/保护。
4. 持久性:事务一旦达成,就永久有效。
JDBC自动管理事务,当调用executeUpdate()时,JDBC会自动提交事务。
如何手动管理事务?
con.setAutoCommit(false);//改为手动提交事务
con.commit(); //提交
con.rollback(); //回滚
public class Test {
/**
* 模拟转账
* 1.验证收款方账户是否存在。
* 2.验证付款方账户余额是否充足。
* 3.付款方账户减N元
* 4.收款方账户加N元。
*/
@Test
public void test1(){
//假设用户在atm上输入了如下信息:
int padId = 1;//付款方账户
int recId = 2;//收款方账号
double mny = 1000.0;//转账金额
Connection con = null;
try {
con = DBUtil.getConnection();
//1.验证收款方账户是否存在。
String sql = "SELECT * FROM accounts "
+ "WHERE id=?";
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, recId);
ResultSet rs = ps.executeQuery();
if(!rs.next()){
System.out.println("收款账号不存在!");
throw new SQLException("收款账号不存在!");
}
double recMoney = rs.getDouble("money");//记录收款方余额,便于之后转
账计算余额
//2.验证付款方账户余额是否充足。
sql = "SELECT * FROM accounts "
+ "WHERE id=? ";
ps = con.prepareStatement(sql);
ps.setInt(1, padId);
rs = ps.executeQuery();
rs.next();
double payMoney = rs.getDouble("money");
if(payMoney<mny){
System.out.println("余额不足!");
throw new SQLException("余额不足!");
}
//设置手动管理事务(取消自动提交事务)
con.setAutoCommit(false);
//3.付款方账户减N元
sql = "UPDATE accounts "
+ "SET money=?"
+ "WHERE id=?";
ps = con.prepareStatement(sql);
ps.setDouble(1, payMoney-mny);
ps.setInt(2, padId);
ps.executeUpdate();
//假设此处有个错误!
//Integer.valueOf("abc");
//4.收款方账户加N元。
sql = "UPDATE accounts "
+ "SET money=? "
+ "WHERE id=?";
ps = con.prepareStatement(sql);
ps.setDouble(1, recMoney+mny);
ps.setInt(2, recId);
ps.executeUpdate();
//当账户转账流程正常结束时统一提交事物!
con.commit();
} catch (Exception e) {
//当转账过程发生异常时回滚事物!
try {
con.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
throw new RuntimeException("回滚失败!");
}
e.printStackTrace();
throw new RuntimeException("转账失败!",e);
}finally{
DBUtil.close(con);
}
}
/**
* 批量添加员工
*/
@Test
public void test2(){
Connection con = null;
try {
con = DBUtil.getConnection();
con.setAutoCommit(false);//取消自动提交事务
//批量发送数据的前提是他们的SQL一样
String sql = "INSERT INTO emp "
+ "VALUES (seq_emp.nextval,?,?,?,?,?,?,?)";
PreparedStatement ps = con.prepareStatement(sql);
for(int i=1;i<=108;i++){
ps.setString(1, "好汉"+i);
ps.setString(2, "打劫");
ps.setInt(3, 0);
ps.setDate(4, null);
ps.setDouble(5, 1000.0);
ps.setDouble(6, 8000);
ps.setInt(7, 3);
//将本条数据暂存到ps内
ps.addBatch();
//每个n次批量发送一次数据
if(i%30==0){
ps.executeBatch();
//清除缓存的数据,便于下次发送
ps.clearBatch();
}
}
//余下的数据单独发送一次
ps.executeBatch();
con.commit();
} catch (SQLException e) {
try {
con.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
throw new RuntimeException("回滚失败!");
}
e.printStackTrace();
throw new RuntimeException("批量添加员工失败!",e);
}finally{
DBUtil.close(con);
}
}
/**
* 添加部门,然后向此部门内添加员工
*/
@Test
public void test3(){
//假设要添加的部门信息如下
String dname = "财务";
String loc = "北京";
//假设要添加的员工数据如下
String ename = "张三";
String job = "经理";
double sal = 6000;
double comm = 546;
String ename2 = "李四";
String job2= "职员";
double sal2 = 4000;
double comm2 = 1546;
Connection con = null;
try {
con = DBUtil.getConnection();
con.setAutoCommit(false);
//先添加部门
String sql = "INSERT INTO depts "
+ "VALUES (seq_depts.nextval,?,?)";
//参数2是一个数组,存的是希望被ps记住的字段的名称。
PreparedStatement ps = con.prepareStatement(sql,new String[]
{"deptno","loc"});
ps.setString(1, dname);
ps.setString(2, loc);
ps.executeUpdate();
/*
* 从ps中获取它之前记录的字段的值
* 返回的结果集中只有一条数据,存的就是记录的那些字段的值
* rs.getXXX(参数);中的参数代表你在数组中所填的第几个参数。
*/
ResultSet rs = ps.getGeneratedKeys();
rs.next();
int deptno = rs.getInt(1);
String dname3 = rs.getString(2);
System.out.println(deptno);
System.out.println(dname3);
//添加员工1
sql = "INSERT INTO emp "
+ "VALUES (seq_emp.nextval,?,?,?,?,?,?,?)";
ps = con.prepareStatement(sql);
ps.setString(1, ename);
ps.setString(2, job);
ps.setInt(3, 0);
ps.setDate(4, null);
ps.setDouble(5,sal);
ps.setDouble(6,comm);
ps.setInt(7,deptno);
ps.executeUpdate();
//添加员工2
sql = "INSERT INTO emp "
+ "VALUES (seq_emp.nextval,?,?,?,?,?,?,?)";
ps = con.prepareStatement(sql);
ps.setString(1, ename2);
ps.setString(2, job2);
ps.setInt(3, 0);
ps.setDate(4, null);
ps.setDouble(5,sal2);
ps.setDouble(6,comm2);
ps.setInt(7,deptno);
ps.executeUpdate();
con.commit();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("添加部门员工信息失败!",e);
}finally{
DBUtil.close(con);
}
}
}