接上篇JDBC技术(一)JDBC基础入门封装了JdbcUtils工具:
public class JdbcUtils {
private static String driverClass = null;
private static String url = null;
private static String userName = null;
private static String password = null;
public static void scanf(String filePath) throws IOException {
Properties pp = new Properties();
InputStream inStream = JdbcUtils.class.getResourceAsStream(filePath);
pp.load(inStream);
driverClass = pp.getProperty("driverClass");
url = pp.getProperty("url");
userName = pp.getProperty("userName");
password = pp.getProperty("password");
}
public static void load() throws ClassNotFoundException {
Class.forName(driverClass);
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, userName, password);
}
public static void close(Connection conn, PreparedStatement state, ResultSet rs) {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
接着又整理了事务的相关基本内容:
数据库事务:
在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。组成各个数据的执行的单元,要么都成功,要么都不成功。
一个最经典的转账的例子 : A给B转100元钱。
- 先给A扣除掉100元 (执行单元)
- 再给B加上100元 (执行单元)
这个过程就需要看成一个事务。
在这个过程中,如果A的钱扣除之后,出现网络异常等问题,就需要回滚事务(回到二者的起始状态)
如果没有出问题那就提交这个事务。 (事务一旦提交就不可回滚)
需要注意的是:
MySQL数据库的事务是默认自动提交的。
JDBC处理事务:
当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,
而不能回滚,为了让多个 SQL 语句作为一个事务执行:
- 调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
- 在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
- 在出现异常时,调用 rollback(); 方法回滚事务
- 若此时 Connection 没有被关闭, 则需要恢复其自动提交状态
转账问题演示,不控制事务情况下模拟异常:
public class Test4 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement state = null;
try {
JdbcUtils.scanf("/db.properties");
JdbcUtils.load();
conn = JdbcUtils.getConnection();
//开启事务 设置事务不自动提交只能手动提交
//conn.setAutoCommit(false);
String sql = "update t_user set money = money + ? where username=?";
state = conn.prepareStatement(sql);
state.setDouble(1, -100.0);
state.setString(2, "zhang");
state.executeUpdate();
//模拟异常
int i = 1 / 0;
state.setDouble(1, 100.0);
state.setString(2, "san");
state.executeUpdate();
//能顺利执行结束 提交事务
//conn.commit();
} catch (Exception e) {
//回滚事务
// try {
// conn.rollback();
// } catch (SQLException e1) {
// e1.printStackTrace();
// }
e.printStackTrace();
}finally {
JdbcUtils.close(conn, state, null);
}
}
}
执行结果:
解决上述问题,需要将两个执行单元放在同一个事务当中。若带上事务在执行一次结果显示如下:
可以说显示的已经很明显了。
事务的特性:
原子性 ---强调的是事务的不可分割的特性。 一个事务已经是最小单元了。你不能说在一个事务中再分出几个子事务。
一致性 -‐‐强调的是事务执行前后数据需要保证一致。就像上面的转账事务,无论成功与否,他两的总金额是不变的。
隔离性 ‐‐-强调的是多个事务同时操作一条记录,事务之间不能互相干扰。比如A开一个事务,B也开一个事务,但AB之间互相不能干扰。
持久性 ‐‐-强调的是事务一旦结束了,数据将永久的保存到数据库中。
不考虑事务的隔离性会引发的问题:
脏读: 对于两个事务 T1,T2。T1 读取了已经被 T2 更新但还没有被提交的字段。之后,若 T2 回滚,T1读取的内容就是临时且无效的。
不可重复读: 对于两个事务 T1, T2。 T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.强调的是update,修改记录的数据。
幻读: 对于两个事务 T1,T2。 T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行之后, 如果 T1 再次读取同一个表, 就会多出几行。强调是insert,向表中添加一条数据。
事务的隔离级别(设置数据库的隔离级别,根据级别的不同,解决上述的读的问题)
读未提交(Read uncommitted) ‐‐ 什么读都解决不了
读已提交(Read committed) ‐‐ 避免脏读,但是不可重复读和幻读有可能产生
可重复读(Repeatable read) ‐‐ 避免脏读和不可重复读,但幻读有可能产生的
可串行化(Serializable) ‐‐ 避免各种读
数据库都有自己默认的隔离级别 MySQL的数据库,默认的隔离级别是Repeatable read,避免脏读和不可重复读。
数据库连接池的配置使用:
DataSource接口,SUN公司提供的接口,任何的连接池(开源的,自定义的)必须要实现该接口。我们可以通过该接口的getConnection()方法就能从连接池中获取到连接
4个核心参数的设置:任何的开源的连接池,4大参数都需要自己来设置(驱动名称,数据库连接,用户名,密码)。
以Druid为例,方式一(修改JdbcUtils中相关内容):
private static DruidDataSource dataSource = null;
public static void initDataSource(){
dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setUsername(userName);
dataSource.setPassword(password);
//初始化连接数
dataSource.setInitialSize(5);
//最大连接数
dataSource.setMaxActive(10);
//最大等待时长 毫秒
dataSource.setMaxWait(2000);
}
方式二:配置文件方式properties文件中可进行如下基本配置:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///jdbcdemo
username=root
password=root
initialSize=5
maxActive=10
maxWait=2000
public class JdbcUtils {
private static DataSource dataSource = null;
static {
Properties pp = new Properties();
InputStream inStream = JdbcUtils.class.getResourceAsStream("/druid.properties");
try {
pp.load(inStream);
dataSource = DruidDataSourceFactory.createDataSource(pp);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
}