一、JDBC事务管理
事务的描述:
事务指的是逻辑上的一组操作,组成这组操作各个逻辑单元要么全都成功,要么全都失败。
为什么使用事务?
例如:下面的代码:
public void shiwu(){
Connection conn = null;//连接
PreparedStatement pstam = null;//执行者
ResultSet rs = null;//结果集
try {
conn = JDBCUtils.buildConnection();//获取连接
//sql语句
String sql = "update yinhang set price = price+? where id=?";
//预处理
pstam = conn.prepareStatement(sql);
//赋值
pstam.setInt(1, -100);
pstam.setInt(2, 1);
//执行语句
int i = pstam.executeUpdate();
//异常发生
int a = 1/0;
pstam.setInt(1, +100);
pstam.setInt(2, 2);
int i2 = pstam.executeUpdate();
if (i == i2) {
System.out.println("转账成功");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.release(conn, pstam);
}
}
注意:假如这是一个银行存取款,在这个时候异常发生,会导致前一个人的钱已经扣除,后一个人的钱还没有加上,导致数据丢失,存在数据的不完整性。
在这种情况下,就需要使用事务。
在下列代码就对上述代码进行了修正
@Test
public void shiwu(){
Connection conn = null;//连接
PreparedStatement pstam = null;//执行者
ResultSet rs = null;//结果集
try {
conn = JDBCUtils.buildConnection();//获取连接
//开启事务
conn.setAutoCommit(false);
//sql语句
String sql = "update yinhang set price = price+? where id=?";
//预处理
pstam = conn.prepareStatement(sql);
//赋值
pstam.setInt(1, -100);
pstam.setInt(2, 1);
//执行语句
int i = pstam.executeUpdate();
//异常发生
int a = 1/0;
pstam.setInt(1, +100);
pstam.setInt(2, 2);
int i2 = pstam.executeUpdate();
if (i == i2) {
System.out.println("转账成功");
}
//提交
conn.commit();
} catch (Exception e) {
e.printStackTrace();
try {
//回滚
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
JDBCUtils.release(conn, pstam);
}
}
注意:事务是Connection的方法,setAutoCommit()方法:将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式下,则它的所有 SQL 语句将被执行并作为单个事务提交。所以参数需要写成false。
二、Druid实现连接池
可以与Spring框架进行快速的整合:相当于一个容器
代码实现:
配置文件:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db1
username=root
password=123456
工具类:这个工具类在jdbc的使用中可见
测试类:
@Test
public void druidTest(){
Connection conn = null;
PreparedStatement pstam = null;
ResultSet rs = null;
try {
//创建Properties对象
Properties properties = new Properties();
//读取文件
properties.load(new FileInputStream("src/druid.properties"));
//获取连接池对象
DataSource createDataSource = DruidDataSourceFactory.createDataSource(properties);
//建立连接
conn = createDataSource.getConnection();
String sql = "insert into yinhang values (null,?,?)";
pstam = conn.prepareStatement(sql);
pstam.setString(1, "张飞");
pstam.setInt(2, 1000);
int i = pstam.executeUpdate();
if (i>0) {
System.out.println("插入成功");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.release(conn, pstam);
}
}
注意:
- 在上述代码中依然需要释放Connection,虽然调用的依然是.close()方法,但其实被DruidDataSource包装,当释放时会归还到连接池中,而不是释放
- Druid配置步骤:先拿连接池,再建立连接
- DruidDataSourceFactory.creatDataSource(properties对象):直接拿到数据库连接
三、c3p0实现连接池
注意:c3p0实现了一个功能,就是在生成连接池的时候,如果没有指定xml文件,将会自动的去src目录下查找一个名字为c3p0-config.xml的文件。所以在下列的代码中看不到任何加载驱动和连接数据库的代码。
连接池不能一直创建,否则就会造成资源的浪费
实现:
c3p0-config.xm:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///db1</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">5</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>
工具类JDBCUtils2
public class JDBCUtils2 {
private static final ComboPooledDataSource source = new ComboPooledDataSource();
//建立连接
public static Connection getConnection() throws SQLException{
return source.getConnection();
}
//获取连接池
public static DataSource getDataSource(){
return source;
}
//释放资源(这是对于查的资源释放)
public static void release(Connection conn,Statement stat,ResultSet rs){
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
stat = null;
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rs = null;
}
}
//这里使用了方法的重载,来实现对增删改进行资源的释放
public static void release(Connection conn,Statement stat){
if (conn != null) {
try {
//这里虽然依然是close,但是被装饰了,此时调用的close方法将会将连接返回到连接池
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
stat = null;
}
}
}
注意:在这个工具类中需要着重的理解一下问什么使用close()方法来返回连接,在代码的注释中有解释
测试类C3p0Demo :
public class C3p0Demo {
@Test
public void c3p0Test(){
Connection conn = null;
PreparedStatement prstm = null;
ResultSet rs = null;
try {
//调用工具类,从连接池中获取连接
conn = JDBCUtils2.getConnection();
//写sql语句
String sql = "insert into yinhang values(null,?,?)";
//预编译
prstm = conn.prepareStatement(sql);
//赋值
prstm.setString(1, "小乔");
prstm.setInt(2, 2000);
//执行
int i = prstm.executeUpdate();
if (i > 0) {
System.out.println("插入成功");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//返回连接
JDBCUtils.release(conn, prstm);
}
}
}
未完待续,剩下内容明天更。