JDBC处理事务与数据库连接池
Posted shi_zi_183
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC处理事务与数据库连接池相关的知识,希望对你有一定的参考价值。
JDBC处理事务与数据库连接池
JDBC处理事务
在数据库操作中,一项事务是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单位。只有当事务中的所有操作都正常完成,整个事务才能被提交到数据库中,如果一项操作没有完成,则整个事务会被撤销。
针对JDBC处理事务的操作,在Connection接口中,提供了三个相关的方法,
1)setAutoCommit(boolean autoCommit)设置是否自动提交事务
2)commit()提交事务
3)rollback()撤销事务
在上述三个方法中,默认情况下,事务是自动提交的。也就是说,如果每一条操作数据库的SQL语句执行成功,系统会自动调用commit()方法来提交事务,否则就自动调用rollback()撤销事务。
1)创建一个新数据库chapter02,并在该数据库中创建名为account的表,向表中插入若干条数据
create database chapter02;
use chapter02;
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
insert into account(name,money)values('aaa',1000);
insert into account(name,money)values('bbb',1000);
insert into account(name,money)values('ccc',1000);
2)新建工程JDBC_commit,新建一个类Ex01,该类用于模拟两个账号之间的转账业务
package JDBC_commit;
import java.sql.*;
import JDBC.JDBCUtils;
public class Ex01 {
public static void main(String[] args){
String outAccount="aaa";
String inAccount="bbb";
double amount=200;
Connection conn=null;
PreparedStatement prestmt1=null;
PreparedStatement prestmt2=null;
try{
conn=JDBCUtils.getConnection();
conn.setAutoCommit(false);
String sql="update account set money=money-? where"
+" name=? and money>=?";
prestmt1=conn.prepareStatement(sql);
prestmt1.setDouble(1, amount);
prestmt1.setString(2, outAccount);
prestmt1.setDouble(3, amount);
prestmt1.executeLargeUpdate();
String sql2="update account set money=money+? where"
+" name=?";
prestmt2=conn.prepareStatement(sql2);
prestmt2.setDouble(1, amount);
prestmt2.setString(2, inAccount);
prestmt2.executeLargeUpdate();
conn.commit();
System.out.println("转账成功");
}catch(Exception e){
try{
e.printStackTrace();
conn.rollback();
System.out.println("转账失败");
}catch(SQLException e1){
e1.printStackTrace();
}
}finally{
if(prestmt1!=null){
try{
prestmt1.close();
}catch(SQLException e){
e.printStackTrace();
}
prestmt1=null;
}
if(prestmt2!=null){
try{
prestmt2.close();
}catch(SQLException e){
e.printStackTrace();
}
prestmt2=null;
}
if(conn!=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
conn=null;
}
}
}
}
需要注意的是,将setAutoCommit()方法的参数设置为false后,事务必须使用commit()方法提交,而事务的回滚不一定显式执行,如果程序最后没有执行conn.commit(),事务也会回滚,一般是直接抛出异常,终止程序的正常执行。因此通常情况下,conn.rollback()语句放在catch语句中执行。
数据库连接池
前面学习了JDBC的基本用法,由于每操作一次数据库,都会执行一次创建和断开Connection对象的操作,影响数据库的访问效率,为了解决这个问题,JDBC提供了一个高级操作——数据库连接池
什么是数据库连接池
在JDBC编程中,每次创建和断开Connection对象都会消耗一定的时间和IO资源,这是因为在Java程序与数据库之间建立连接时,数据库端要验证用户名和密码,并且要为这个连接分配资源,Java程序则要把代表连接的java.sql.Connection对象等加载到内存中,所以建立数据库连接的开销很大。尤其是在大量并发访问时,假如某个网站一天的访问量是10万,那么,该网站的服务器就需要创建、断开连接10万次,频繁地创建、断开连接势必会影响数据库地访问效率,甚至导致数据库崩溃。
为了解决问题,工程师提出了数据库连接池。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用现有的数据库连接,而不是重新建立。
数据库连接池在初始化时,将创建一定数量的数据库连接放到连接池中,当应用程序访问数据库时并不是直接创建Connection,而是向连接池申请一个Connection。如果连接池有空闲的Connection,则将其返回,否则创建新的Connection使用。使用完毕后,连接池会将该Connection回收,并交付其他的线程使用,以减少创建和断开数据库连接的次数,提高数据库的访问效率。
DataSource接口
为了获取数据库连接对象,JDBC提供了javax.sql.DataSource接口,它负责与数据库建立连接,并定义返回值为Connection对象的方法
Connection getConnection()
Connection getConnection(String username,String password)
接口通常都会有其实现类,javax.sql.DataSource接口也不例外,人们习惯将javax.sql.DataSource接口的实现类称为数据源。
DBCP数据源
DBCP时数据库连接池(DataBase Connection Pool)的简称,是Apache组织下的开源连接池实现,也是Tomcat服务器使用的连接池组件。单独使用DBCP数据源时,需要在应用程序中导入两个jar包。
commons-dbcp.jar包
commons-dbcp.jar包是DBCP数据源的实现包,包含所有操作数据库连接信息和数据库连接池初始化信息的方法,并实现了DataSource接口的getConnection()方法。
commons-pool.jar包
commons-pool.jar包是commons-dbcp.jar包的依赖包。
在上面两个jar包中,commons-dbcp.jar包中包含两个核心的类,分别是BasicDataSourceFactory和BasicDataSource,它们都包含获取DBCP数据源对象的方法。
BasicDataSource是DataSource接口的实现类,主要包括设置数据源对象的方法
方法名称 | 功能描述 |
---|---|
void setDriverClassName(String driverClassName) | 设置连接数据库的驱动名称 |
void setUrl(String url) | 设置连接数据库的路径 |
void setUsername(String username) | 设置数据库的登录账号 |
void setPassword(String password) | 设置数据库的登录密码 |
void setInitialSize(int initialSize) | 设置数据库连接池初始化的连接数目 |
void setMaxActive(int maxIdle) | 设置数据库连接池最大活跃的连接数目 |
void setMinIdle(int minIdle) | 设置数据库连接池最小闲置的连接数目 |
Connection getConnection() | 从连接池中获取一个数据库连接 |
BasicDataSourceFactory是BasicDataSource的工厂类,它包含一个返回值为BasicDataSource对象的方法createDataSource(),该方法通过读取配置文件的信息生成数据源对象并返回给调用者。这种把数据库的连接信息和数据源的初始化信息提取出来写进配置文件的方式,让代码看起来更加简洁,思路更加清晰。
1、通过BasicDataSource类直接创建数据源对象
使用BasicDataSource类创建一个数据源对象,手动给数据源对象设置属性值,然后获取数据库连接对象。
1)新建Ex02类
Ex02.java
package JDBC_commit;
import java.sql.*;
import javax.sql.*;
import org.apache.commons.dbcp.*;
public class Ex02 {
public static DataSource ds=null;
static{
BasicDataSource bds=new BasicDataSource();
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql://localhost:3306/chapter02");
bds.setUsername("root");
bds.setPassword("123456");
bds.setInitialSize(5);
bds.setMaxActive(5);
ds=bds;
}
public static void main(String[] args)throws SQLException{
Connection conn=ds.getConnection();
DatabaseMetaData metData=conn.getMetaData();
System.out.println(metData.getURL()
+",Username="+metData.getUserName()
+",DriverName="+metData.getDriverName()
);
}
}
2、通过读取配置文件创建数据源对象
使用BasicDataSourceFactory工厂类读取配置文件,创建数据源对象,然后获取数据库连接对象。
1)创建dbcpconfig.properties文件,用于设置数据库的连接信息和数据源的初始化信息
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/chapter02
username=root
password=123456
initialSize=5
maxActive=10
maxIdile=10
2)创建Ex03类
Ex03.java
package JDBC_commit;
import java.util.*;
import java.io.*;
import java.sql.*;
import javax.sql.*;
import org.apache.commons.dbcp.*;
public class Ex03 {
public static DataSource ds=null;
static{
Properties prop=new Properties();
try{
InputStream in=new Ex03().getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");
prop.load(in);
ds=BasicDataSourceFactory.createDataSource(prop);
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args)throws SQLException{
Connection conn=ds.getConnection();
DatabaseMetaData metaData=conn.getMetaData();
System.out.println(metaData.getURL()
+",Username="+metaData.getUserName()
+",DriverName="+metaData.getDriverName()
);
}
}
C3P0数据源
C3P0数据源是目前最流行的开源数据库连接池之一,它实现了DataSource数据源接口,支持JDBC2和JDBC3的标准规范,易于拓展并且性能优越,著名的开源框架Hibernate和Spring使用的都是该数据源。
C3P0中DataSource接口的实现类ComboPooledDataSource,它是C3P0的核心类,它提供了数据源对象的相关方法
需要注意的是c3p0包依赖mchange-commons-java包需要一并引入
方法名称 | 功能描述 |
---|---|
void setDriverClass() | 设置连接数据库的驱动名称 |
void setJdbcUrl() | 设置连接数据库的路径 |
void setUser() | 设置数据库的登录账号 |
void setPassword() | 设置数据库的登录密码 |
void setMaxPoolSize() | 设置数据库连接池最大的连接数目 |
void setMinPoolSize() | 设置数据库连接池最小的连接数目 |
void setInitialPoolSize() | 设置数据库连接池初始化的连接数目 |
Connection getConnection() | 从连接池中获取一个数据库连接 |
当使用C3P0数据源时,首先要创建数据源对象,创建数据源对象可以使用ComboPoolDataSource类,该类有两个构造方法,分别是ComboPoolDataSource()和ComboPoolDataSource(String configName)。
1、通过ComboPoolDataSource类直接创建数据源对象
在工程中导入mysql-connector-java.5.0.8-bin.jar
Ex04.java
package JDBC_commit;
import java.sql.*;
import javax.sql.*;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Ex04 {
public static DataSource ds=null;
static{
ComboPooledDataSource cpds=new ComboPooledDataSource();
try{
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/chapter02");
cpds.setUser("root");
cpds.setPassword("123456");
cpds.setInitialPoolSize(5);
cpds.setMaxPoolSize(15);
ds=cpds;
}catch(Exception e){
throw new ExceptionInInitializerError(e);
}
}
public static void main(String[] atgs)throws SQLException{
System.out.println(ds.getConnection());
}
}
2、通过读取配置文件创建数据源对象
通过ComboPoolDataSource(String configName)构造方法读取c3p0-config.xml配置文件,创建数据源对象,然后获取获取数据库连接对象。
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="user">root</property>
<property name="password">123456</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
jdbc:mysql://localhost:3306/chapter02</property>
<property name="checkoutTimeout">30000</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<name-config name="myconfig">
<property name="user">root</property>
<property name="password">123456</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
jdbc:mysql://localhost:3306/chapter02</property>
<property name="initialPoolSize">5</property>
<property name="maxIdleTime">15</property>
</name-config>
</c3p0-config>
c3p0-config.xml配置了两套数据源,<default-config>是默认配置,在没有指定配置时默认使用该配置创建c3p0数据源对象,<name-config name=“myconfig”>是自定义数据源,一个配置文件可以有多个数据源,当用户需要使用自定义配置时,调用ComboPooledDataSource(String configName)方法,传入节点中的name属性值即可创建C3P0数据源对象。
Ex05.java
package JDBC_commit;
import java.sql.*;
import javax.sql.*;
import com.mchange.v2.c3p0.*;
public class Ex05 {
public static DataSource ds=null;
static{
ComboPooledDataSource cpds=new ComboPooledDataSource("myconfig");
ds=cpds;
}
public static void main(String[] args)throws SQLException{
System.out.print(ds.getConnection());
}
}
需要注意以这种方式创建对象必须遵循一下两点。
1)配置文件名称必须为c3p-config.xml,并且位于该项目的src根目录下。
2)当传入的configName值为空或者不存在时,则会使用默认的配置方式创建数据源。
以上是关于JDBC处理事务与数据库连接池的主要内容,如果未能解决你的问题,请参考以下文章
JDBC第三天~JDBC之事务批处理自动生成主键连接池重构设计
Java 微服务 乐优网络商城 day02 源代码 SpringBoot 实战开发 整合JDBC和事务(数据库连接池)