10-27 程宗武
10-27(JDBC)
/*
JDBC本质: 官方定义的一套操作所有关系型数据库的规则,即接口
各个厂商根据各自的jar包去实现这个接口,真正执行的代码大是驱动jar包中的实现类
*/
/*
JDBC各个对象的作用:
1.DriverManager:驱动管理对象
*功能:
1.注册驱动:
static void registerDriver(Driver driver, DriverAction da)注册与给定的驱动程序 DriverManager 。
写代码使用: lass.forName("com.mysql.cj.jdbc.Driver");
2.获取数据库链接
2.Connection:数据库连接对象
3.Statement:执行SQL的对象
用于执行静态SQL语句并返回其生成的结果的对象。默认情况下,每个 Statement对象只能有一个 ResultSet对象同时打开。
4.ResultSet:结果集对象
5.PrepareStatement:执行SQL的对象
表示预编译的SQL语句的对象,SQL语句已预编译并存储在 PreparedStatement对象中。 然后可以使用该 对象多次有效地执行此语句.该接口继承自 Statement 接口,扩展了以下常用方法:
void setObject(int parameterIndex, Object x) :使用给定对象设置指定参数的值 ResultSet executeQuery() :执行此 PreparedStatement对象中的SQL查询,并返回查询 PreparedStatement的 ResultSet对象 int executeUpdate() :执行在该SQL语句 PreparedStatement对象,它必须是一个SQL数据操纵 语言(DML)语句,如INSERT , UPDATE或 DELETE ; 或不返回任何内容的SQL语句,例如DDL语 句
*/
//查询数据
@Test
public void test() {
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.66.128:3306/jdbcdemo?useUnicode=true&characterEncoding=utf8", "root", "123456");
String sql = "SELECT u.id,s.id,u.username 用户名,u.password 密码,u.createDate 日期 FROM users u,sutdents s";
PreparedStatement statement = conn.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 0; i < metaData.getColumnCount(); i++) {
System.out.print(metaData.getColumnLabel(i + 1) + "\t");//这里获取的是列的别名,如果要获取原来的列名,使用getColumnName()
}
System.out.println();
while (resultSet.next()) {
System.out.println(
resultSet.getObject("u.id") + "\t" + //这里如果只查询列名为id的列的话,只会查询出Sql语句中写在前面的id
resultSet.getObject("s.id") + "\t" +
resultSet.getObject("用户名") + "\t" +//这里使用了别名的方式进行获取
resultSet.getObject("密码") + "\t" +
resultSet.getObject("日期") + "\t");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
//添加数据
@Test
public void test() {
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.66.128:3306/jdbcdemo?useUnicode=true&characterEncoding=utf8", "root", "123456");;
String sql = "INSERT INTO users(username,password) VALUES (?,?)";
PreparedStatement statement = conn.prepareStatement(sql);
statement.setObject(1, "王五");
statement.setObject(2, "789");
int row = statement.executeUpdate();
if (row > 0) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
/**
* 事务的四大特性
* 原子性:事务中所有操作是不可分割的原子单位。事务中的所有操作要么全部执行成功
* 要么全部执行失败。
* 一致性:事务执行以后数据库状态与其他业务规则保持一致。如转账业务,无论事务是否执行成功,
* 参与转账的两个账号余额之和应该是不变的。
* 隔离性:隔离性是指在并发操作中,不同事务之间因该隔离开来,使每个并发中的事务不会相互
* 干扰
* 持久性:一旦事务提交成功,事务中所有的数据操作都必须持久化到数据库中,
*
*/
/**
* *JDBC中的事物(事务开启之后, 所有的操作都会临时保存到事务日志, 事务日志只有在得到 commit 命令才会同 步到数据 表中,其他任何情况都会清空事务日志( rollback ,断开连接))
* *Connection的三个方法与事务相关(同一事物中所有的操作,都在使用一个Connection对象)
* *setAutoCommit(boolean)设置是否为自动提交事物,如果true(默认为true)表示自动提交,
* 也就是每条执行的SQL语句都是一个单独的事物,如果设置为false,那么就相当于开启了事务(不是关闭事务)
* *commit():提交结束事物
* *rollback():回滚结束事物
* *jdbc处理事务的代码格式:
* try{
* con.setAutoCommit(false);//开启事务
* ...
* con.commit();//最后提交事务
* }catch(){
* conn.rollback();//回滚事务
* }
* *事物的并发读问题
* *脏读:读取到另一个事务未提交的数据
* *不可重复读:两次读取不一致
* *幻读(虚读):读取到另一事务已提交的数据
* *四大隔离级别
* *串行化
* 不会引发任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的,但性能最差
* *可重复读
* 防止脏读和不可重复读,性能优于串行化
* *读已提交的数据
* 防止脏读,性能优于可重复读
* *读未提交数据
* 可能出现任何事务并发问题,性能最好
*/
//没有开启事务时所遇到的问题
@Test
public void test1() {
PreparedStatement ps = null;
try {
Connection conn = DBHelper.getConn();
//王思聪转出100元
String sql = "UPDATE customer SET balance = balance + ? WHERE customerName = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1, -100);
ps.setObject(2, "王思聪");
ps.executeUpdate();
int j = 10 / 0; // 这里抛出了异常,会导致王思聪的账户余额减少了100元,而王健林的账户余额却没有改变原因是抛出了异常,下面的代码不会被执行(原本应该是王健林的账户余额增加100)
//王健林转入100元
ps.setObject(1, 100);
ps.setObject(2, "王健林");
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
//解决办法:开启事务。
@Test
public void test1() {
PreparedStatement ps = null;
Connection conn = null;
try {
conn = DBHelper.getConn();
//王思聪转出100元
//开启事务(设置自动提交为false就是开启事务)
conn.setAutoCommit(false);
String sql = "UPDATE customer SET balance = balance + ? WHERE customerName = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1, -100);
ps.setObject(2, "王思聪");
ps.executeUpdate();
//这里抛出异常
int j = 10 / 0; //这里抛出了异常,这个事务不会被提交并持久化到数据库中,而是回到事务发生前的状态,账户的余额不会发生变化。
//王健林收入100元
ps.setObject(1, 100);
ps.setObject(2, "王健林");
ps.executeUpdate();
//提交事务
} catch (SQLException e) {
e.printStackTrace();
//回滚结束事务(这里数据会回到事务开始前的状态)
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
DBHelper.closeConn();
}
}
回滚点
在某些成功的操作完成之后,后续的操作有可能成功有可能失败,但是不管成功还是失败,前面操作都 已经成功,可以在当前成功的位置设置一个回滚点。可以供后续失败操作返回到该位置,而不是返回所 有操作,这个点称之为回滚点。
设置回滚点语法: savepoint 回滚点名字 ; 回到回滚点语法: rollback to 回滚点名字 ;
@Test
public void test3() {
PreparedStatement ps = null;
Connection conn = null;
Savepoint point = null;
try {
conn = DBHelper.getConn();
String sql = "UPDATE customer SET balance = balance - 100 WHERE id = 1";
ps = conn.prepareStatement(sql);
//让账户减少300
for (int i = 0; i < 3; i++) {
ps.executeUpdate();
}
//设置回滚点
point = conn.setSavepoint();
//抛出一个异常
int m = 10 / 0;
//再次让账户减少300
for (int i = 0; i < 3; i++) {
ps.executeUpdate();
}
//提交事务
conn.commit();
} catch (SQLException e) {
//回滚到指定的回滚点
try {
conn.rollback(point);
conn.commit();//这里需要手动提交一次事务,不然回滚点前的事务没有提交,数据库中的数据也不会发生变化
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
事务隔离
查询全局事务的隔离级别
show variables like '%isolation%';
-- 或
select @@tx_isolation;
设置事务隔离级别
set global transaction isolation level 级别字符串;
-- 如:
set global transaction isolation level read uncommitted;(读未提交)
近期评论