JDBC
结果集元数据
ResultSetMetaData用于描述查询结果的相关信息,其中包含列名称,列数量,类数据类型等.
原理:
使用案例:
public static void main(String[] args) {
Connection conn = null;
try {
conn = DBUtils.getConnection();
String sql = "select * from robin_user";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
//结果集元数据
ResultSetMetaData meta = rs.getMetaData();
int n = meta.getColumnCount();
for (int i = 1; i <=n ; i++) {
// i = 1 ... n
String name = meta.getColumnName(i);
System.out.println(name);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.close(conn);
}
}
作业:
/**
* 打印一个SQL查询结果的全部列名
* @param sql
*/
public static void print(String sql) {
//...
}
JDBC 实务控制
数据库提供了实务控制功能,支持ACID特性.
JDBC提供了API,方便的调用数据库的实务功能,其方法有:
相关API:
- Connection.getAutoCommit():获得当前事务的提交方式,默认为true
- Connection.setAutoCommit():设置事务的的提交属性,参数是
- true:自动提交;
- false:不自动提交;
- Connection.commit():提交事务
- Connection.rollback():回滚事务
API调用模板:
Connection conn = null;
try {
conn = DBUtils.getConnection();
//取消自动提交,后续手动提交
conn.setAutoCommit(false);
//SQL... update
//SQL... update
// 余额不足 抛出异常 throw e;
//SQL... update
conn.commit();
} catch (Exception e) {
e.printStackTrace();
DBUtils.rollback(conn);
} finally {
DBUtils.close(conn);
}
提示:事务API经典的用法是采用如下模板,其中DBUtils.rollback()方法封装了回滚方法,其声明如下:
public static void close(Connection conn) {
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
事务测试案例数据:
create table account(
id int(6),
name varchar(100),
balance float(8,2)
);
insert into account (id,name,balance) values (1,'范老师',500);
insert into account (id,name,balance) values (2,'刘老师',1500);
insert into account (id,name,balance) values (3,'河仙姑',2000);
update account set balance=balance-1000 where id=2;
update account set balance=balance+1000 where id=1;
commit;
rollback;
案例:
public class Demo03 {
public static void main(String[] args) {
pay(3,4,200);
System.out.println("ok");
}
public static void pay(int from,int to,double money) {
String sql1 = "update account set balance=balance+? where id=?";
String sql2 = "select balance from account where id=?";
Connection conn = null;
try {
conn = DBUtils.getConnection();
conn.setAutoCommit(false);
PreparedStatement ps = conn.prepareStatement(sql1);
//减钱
ps.setDouble(1, -money);
ps.setInt(2, from);
int n = ps.executeUpdate();
if(n!=1) {
throw new Exception("扣错了");
}
//增加
ps.setDouble(1, money);
ps.setInt(2, to);
n = ps.executeUpdate();
if(n!=1) {
throw new Exception("加错了");
}
ps.close();
//检查
ps = conn.prepareStatement(sql2);
ps.setInt(1, from);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
double bal = rs.getDouble(1);
if(bal<0) {
throw new Exception("透支");
}
}
conn.commit();
} catch (Exception e) {
e.printStackTrace();
DBUtils.rollback(conn);
} finally {
DBUtils.close(conn);
}
}
}
批量更新
批量更新的优势
- 批处理:发送到数据库作为一个单元执行的一组更新语句
- 批处理降低了应用程序与数据库之间的网络调用
- 相比单个SQL语句的处理,批处理更为有效
用法:
-
addBatch(String sql)
- Statement类的方法,可以将多条sql语句添加Statement对象的SQL语句列表中
-
addBatch()
- PreparedStatement类的方法,可以将多条预编译的sql语句添加到PreparedStatement对象的SQL语句列表中
-
executeBatch()
- 把Statemennt对象或PreparedStatemenn对象语句列表中的所有SQL语句发送给数据库进行处理
-
clearBatch()
- 清空当前SQL语句列表
批量执行DDL
public class Demo04 {
public static void main(String[] args) {
String sql1 = "create table log_01(id int(8),msg varchar(100))";
String sql2 = "create table log_02(id int(8),msg varchar(100))";
String sql3 = "create table log_03(id int(8),msg varchar(100))";
// 执行一批SQL
Connection conn = null;
try {
conn = DBUtils.getConnection();
Statement st = conn.createStatement();
// sql1 添加到Statement的缓存中
st.addBatch(sql1);
st.addBatch(sql2);
st.addBatch(sql3);
// 执行一批SQL
int[] ary = st.executeBatch();
System.out.println(Arrays.toString(ary));
System.out.println("OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.close(conn);
}
}
}
批量插入数据
public class Demo05 {
public static void main(String[] args) {
String sql = "insert into robin_user (id,name,pwd) values (?,?,?)";
Connection conn = null;
try {
conn = DBUtils.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < 100; i++) {
//替换参数
ps.setInt(1, i);
ps.setString(2, "name"+i);
ps.setString(3, "123");
//将参数添加到ps缓冲区
ps.addBatch();
}
//批量执行
int[] ary = ps.executeBatch();
System.out.println(Arrays.toString(ary));
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.close(conn);
}
}
}
防止OutOfMemory
- 如果Preparedstatement对象中的缓存列表包含过多的待处理数据,可能会产生OutOfMerry错误
返回自动主键
JDBC API 提供了返回插入数据期间生成的ID的API,
API方法:
- PrepareStatement ps = con.prepareStatement(sql,列名列表);
- rs = stmt.getGeneratement();
create table r_post(
id int(8) auto_increment primary key ,
content varchar(100),
k_id int(8)
);
create table r_keywords(
id int(8) auto_increment primary key,
word varchar(8)
);
需要执行的SQL:
insert into r_keywords (id,word) values (null,?);
insert into r_post (id,content,k_id) values (null,?,?)
案例:
public class Demo06 {
public static void main(String[] args) {
Connection conn = null;
try {
conn = DBUtils.getConnection();
conn.setAutoCommit(false);
String sql = "insert into r_keywords (id,word) values (null,?)";
String[] cols = {"id"};
//自动生成序号的列名
PreparedStatement ps = conn.prepareStatement(sql,cols);
ps.setString(1, "雾霾");
int n = ps.executeUpdate();
if(n!=1) {
throw new Exception("话题添加失败");
}
//获取自动生成的ID
ResultSet rs = ps.getGeneratedKeys();
int id = -1;
while(rs.next()) {
id = rs.getInt(1);
}
rs.close();
ps.close();
sql = "insert into r_post (id,content,k_id) values (null,?,?)";
ps=conn.prepareStatement(sql);
ps.setString(1, "今天天气不错,晚上有雾霾");
ps.setInt(2, id);
n = ps.executeUpdate();
if(n!=1) {
throw new Exception("天气太糟");
}
conn.commit();
System.out.println("OK");
} catch (Exception e) {
e.printStackTrace();
DBUtils.rollback(conn);
} finally {
DBUtils.close(conn);
}
}
}
作业
- 设计一个方法打印一个SQL查询结果的全部列名
- 利用JDBC事物保护一个汇款业务的完整性
- 批量建立10个表,再批量的向每个插入100条数据
- 向部门表插入一个数据并且返回自动生成的ID