一、JDBC 事务
数据准备:
create table account (
id int primary key auto_increment,
name varchar(20),
money double
);
insert into account values (null, 'aaa', 10000);
insert into account values (null, 'bbb', 10000);
insert into account values (null, 'ccc', 10000);
添加事务管理的API
- void setAutoCommit(boolean autoCommit) 将自动提交模式设置为给定状态
- void commit() 使上一次提交/回滚后进行的更改成为持久更改
- void rollback() 取消在当前事务中进行的所有更改
案例:
public class CommitDemo {
@Test
public void demo(){
Connection connection = null;
PreparedStatement statement = null;
try {
connection = JDBCUtilsNew.getConnection();
// 开启事务
connection.setAutoCommit(false);
// 一个账户扣除,另一个账户添加
String sql = "update account set money = money + ? where name = ?";
statement = connection.prepareStatement(sql);
statement.setDouble(1, -1000);
statement.setString(2, "aaa");
statement.executeUpdate();
// 如果不加事务,这里有异常,就会出现问题
// int i = 1 / 0;
statement.setDouble(1, 1000);
statement.setString(2, "bbb");
statement.executeUpdate();
// 提交事务
connection.commit();
} catch (SQLException throwables) {
// 如果发生了异常,则回滚事务
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
} finally {
JDBCUtilsNew.release(statement, connection);
}
}
}
二、连接池
1、概念
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
连接池的创建和销毁是需要耗费时间的,那么在服务器初始化的时候就初始化一些连接,将连接放入到内存中,使用的时候可以从内存中获取,使用完成后可将连接再放入内存中。这样效率是更高的。
2、原理
3、自定义连接池
- 编写一个类实现DataSource接口
- 重写getConnection方法
- 初始化多个连接在内存中
- 编写归还连接的方法
三、开源连接池
1、Druid连接池
Druid是阿里开源的连接池,使用简单,可以与Spring框架快速整合
jar包下载地址:jar-download.com/artifacts/c…
核心类:DruidDataSource
(1)手动设置方式
public class DruidDemo1 {
@Test
public void demo() {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// connection = JDBCUtilsNew.getConnection();
DruidDataSource dataSource = new DruidDataSource();
// 手动设置数据库连接的参数
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/web_test");
dataSource.setUsername("root");
dataSource.setPassword("*****");
// 获取连接
connection = dataSource.getConnection();
// 执行
String sql = "select * from user where id=5;";
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println("[id]:" + resultSet.getInt("id") + " "
+ "[username]:" + resultSet.getString("username") + " "
+ "[password]:" + resultSet.getString("password") + " "
+ "[nickname]:" + resultSet.getString("nickname") + " "
+ "[age]:" + resultSet.getInt("age"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtilsNew.release(statement, connection, resultSet);
}
}
}
(2)配置文件方式
druid.properties 配置文件,注意这里小写!!!
driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/web_test
username = root <!--注意这里是小写-->
password = **** <!--注意这里是小写-->
public class DruidDemo2 {
@Test
public void demo() {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src/druid.properties"));
// 使用连接池,从属性文件中获取数据库连接的参数
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 获取连接
connection = dataSource.getConnection();
// 执行
String sql = "select * from user where id=5;";
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println("[id]:" + resultSet.getInt("id") + " "
+ "[username]:" + resultSet.getString("username") + " "
+ "[password]:" + resultSet.getString("password") + " "
+ "[nickname]:" + resultSet.getString("nickname") + " "
+ "[age]:" + resultSet.getInt("age"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtilsNew.release(statement, connection, resultSet);
}
}
}
2、C3P0连接池
jar包下载地址:sourceforge.net/projects/c3…
(1)手动设置方式
public class C3P0Demo1 {
@Test
public void demo() {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// 获得连接
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 手动设置数据库连接的参数
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/web_test");
dataSource.setUser("root");
dataSource.setPassword("root1234");
// 获取连接
connection = dataSource.getConnection();
// 执行
String sql = "select * from user where id=5;";
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println("[id]:" + resultSet.getInt("id") + " "
+ "[username]:" + resultSet.getString("username") + " "
+ "[password]:" + resultSet.getString("password") + " "
+ "[nickname]:" + resultSet.getString("nickname") + " "
+ "[age]:" + resultSet.getInt("age"));
}
} catch (Exception e){
e.printStackTrace();
} finally {
JDBCUtilsNew.release(statement, connection, resultSet);
}
}
}
(2)配置文件方式
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 使用默认的配置读取连接池对象-->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/web_test</property>
<property name="user">root</property>
<property name="password">root1234</property>
<!-- 连接池参数 -->
<!-- 初始连接数 -->
<property name="initialPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">10</property>
<!-- 最大等待时间 -->
<property name="checkoutTimeout">2000</property>
<!-- 最大空闲回收时间 -->
<property name="maxIdleTime">1000</property>
</default-config>
</c3p0-config>
public class C3P0Demo2 {
@Test
public void demo() {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// 读取配置文件设置参数的方式-自动会加载配置文件
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 获取连接
connection = dataSource.getConnection();
// 执行
String sql = "select * from user where id=5;";
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println("[id]:" + resultSet.getInt("id") + " "
+ "[username]:" + resultSet.getString("username") + " "
+ "[password]:" + resultSet.getString("password") + " "
+ "[nickname]:" + resultSet.getString("nickname") + " "
+ "[age]:" + resultSet.getInt("age"));
}
} catch (Exception e){
e.printStackTrace();
} finally {
JDBCUtilsNew.release(statement, connection, resultSet);
}
}
}
3、改写工具类
public class JDBCUtilsNew2 {
// 创建一个连接池,只需要创建一次即可
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();
/**
* 获得连接的方法
*/
public static Connection getConnection() {
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return connection;
}
public static DataSource getDataSource() {
return dataSource;
}
/**
* 释放资源的方法
*/
public static void release(Statement statement, Connection connection) {
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
statement = null;
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
connection = null;
}
}
public static void release(Statement statement, Connection connection, ResultSet resultSet) {
release(statement, connection);
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
resultSet = null;
}
}
}
测试工具类:
public class C3P0Demo3 {
@Test
public void demo() {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// 获取连接
connection = JDBCUtilsNew2.getConnection();
// 执行
String sql = "select * from user where id=5;";
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println("[id]:" + resultSet.getInt("id") + " "
+ "[username]:" + resultSet.getString("username") + " "
+ "[password]:" + resultSet.getString("password") + " "
+ "[nickname]:" + resultSet.getString("nickname") + " "
+ "[age]:" + resultSet.getInt("age"));
}
} catch (Exception e){
e.printStackTrace();
} finally {
JDBCUtilsNew2.release(statement, connection, resultSet);
}
}
}
四、DBUtils
1、概述
Apache提供的对JDBC进行简单封装的开源工具类库
下载地址: commons.apache.org/proper/comm…
2、常用API之QueryRunner
QueryRunner:核心运行类
构造函数:
- QueryRunner()
- 创建一个与数据库无关的QueryRunner对象
- 这种情况下,调用update或query方法时,需要传入对应的connection参数
- 这种连接数据库的方法适合操作事务
- QueryRunner(DataSource ds)
- 创建一个与数据库关联的QueryRunner对象
- 带连接池的参构造方法 无须传递连接对象。并且可以自动打开、关闭连接
成员方法:
- int update(String sql, Object... params)
- 执行插入、更新或删除(DML)操作
- int update(Connection conn, String sql, Object... params)
- 执行插入、更新或删除(DML)操作
- T query(String sql, ResultSetHandler rsh, Object... params)
- 执行查询操作,需要一个或多个替换参数来执行已给予的SELECT语句,返回一种结果对象
- T query(Connection conn, String sql, ResultSetHandler rsh, Object... params)
- 支持事务,需要一个或多个替换参数来执行已给予的SELECT语句,返回一种结果对象
使用技巧:
- 一般情况下执行CRUD操作使用:
- 构造:QueryRunner(DataSource ds)
- 方法:update(String sql, Object... params)
- 方法:query(String sql, ResultSetHandler rsh, Object... params)
- 如果有事务管理,使用另一套CRUD:
- 构造:QueryRunner()
- 方法:update(Connection conn, String sql, Object... params)
- 方法:query(Connection conn, String sql, ResultSetHandler rsh, Object... params)
3、常用API之DbUtils
常用方法:
- static void commitAndCloseQuietly(Connection conn)
- 提交连接然后关闭连接
- static void rollbackAndCloseQuietly(Connection conn)
- 回滚事务,并安静的关闭连接
4、DBUtils进行增删改
public class DbUtilsDemo1 {
@Test
public void demo1() { // 增加操作
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
queryRunner.update("insert into account values (null,?,?)", "dddd", 2800);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Test
public void demo2() { // 删除操作
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
queryRunner.update("delete from account where id=?", 3);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Test
public void demo3() { // 修改操作
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
queryRunner.update("update account set money=? where id=?", 9999, 4);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
5、DBUtils进行查询
public class DbUtilsDemo2 {
// 查询一条记录
@Test
public void demo1(){
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
Account accountResult = queryRunner.query("select * from account where name=?", new ResultSetHandler<Account>() {
@Override
public Account handle(ResultSet resultSet) throws SQLException {
Account account = new Account();
while (resultSet.next()){
account.setId(resultSet.getInt("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getDouble("money"));
}
return account;
}
},"dddd");
System.out.println(accountResult);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 查询多条记录
@Test
public void demo2(){
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
List<Account> accountResult = queryRunner.query("select * from account;", new ResultSetHandler<List<Account>>() {
@Override
public List<Account> handle(ResultSet resultSet) throws SQLException {
List<Account> result = new ArrayList<>();
while (resultSet.next()){
Account account = new Account();
account.setId(resultSet.getInt("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getDouble("money"));
result.add(account);
}
return result;
}
});
System.out.println(accountResult);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
6、ResultSetHandler实现类
*用来把结果集转换为相应的类型!
(1)ArrayHandler
将查询到的一条记录封装到一个数组中。数组的类型是Object。
(2)ArrayListHandler
将查询到的多条记录封装到一个装有Object[]的List集合中。
public class DbUtilsDemo3 {
@Test
public void demo1() {
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
Object[] result = queryRunner.query("select * from account where name=?", new ArrayHandler(), "dddd");
System.out.println(Arrays.toString(result));
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// // 查询多条记录的操作
@Test
public void demo2() {
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
List<Object[]> result = queryRunner.query("select * from account", new ArrayListHandler());
for (Object[] objects : result) {
System.out.println(Arrays.toString(objects));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
(3)BeanHandler
将查询到的一条记录封装到一个JavaBean中。
(4)BeanListHandler
将查询到的多条记录封装到一个装有JavaBean的List集合中。
public class DbUtilsDemo4 {
@Test
public void demo1() { // 查询一条记录,封装成Bean对象
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
Account account = queryRunner.query("select * from account where name=?",
new BeanHandler<>(Account.class), "dddd");
System.out.println(account);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Test
public void demo2() { // 查询一组记录,封装成Bean List
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
List<Account> accounts = queryRunner.query("select * from account",
new BeanListHandler<Account>(Account.class));
for (Account account : accounts) {
System.out.println(account);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
(5)MapHandler
MapHandler:Map的key是列名,value是那一列记录的值
(6)MapListHandler
MapListHandler:封装到有Map的List集合中
public class DbUtilsDemo5 {
@Test
public void demo1() { // 查询一条记录,封装到Map集合
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
Map<String, Object> result = queryRunner.query("select * from account where name=?",
new MapHandler(), "dddd");
System.out.println(result);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Test
public void demo2() { // 查询一条记录,封装到有Map集合的List中
QueryRunner queryRunner = new QueryRunner(JDBCUtilsNew2.getDataSource());
try {
List<Map<String, Object>> mapList = queryRunner.query("select * from account", new MapListHandler());
for (Map<String, Object> map : mapList) {
System.out.println(map);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
(7)其他实现类
- ColumnListHandler
- 将数据中的某一列封装到List集合中
- 如:select name ... 将name放入集合中
- ScalarHandler
- 单值封装:将查询到的单个值进行封装
- 通常用于select count(*)...语句,返回一个object
- KeyedHandler
- 将多条记录封装到一个装有Map集合的Map集合中,外层的Map的key是可以指定的