一、Druid连接池
1、连接池
连接池可以理解为存放多个连接的集合。是为了解决建立数据库连接耗费资源和时间很多的问题,提高性能。
- Java为数据库连接池提供了公共的接口:
javax.sql.DataSource
,各个厂商需要让自己的连接池实现这个接口,这样应用程序就可以方便的切换不同厂商的连接池。
2、常见连接池技术:
- DBCP(DataBase Connection Pool)数据库连接池,是java数据库连接池的一种,由Apache开发,通过数据库连接池,可以让程序自动管理数据库连接的释放和断开。
- Druid 是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。
- C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
- Tomcat-JDBC是Spring Boot中自动配置优先级最高的连接池方案,它的出现是用来替代Apache早期的连接池产品——DBCP 1.x。
- HikariCP同样是一个十分快速、简单、可靠的及十分轻量级的连接池,只有130KB,在GitHub上看到的是"光HikariCP"的名称,光就是说明它十分快。
3、不同连接池对比
- 在并发比较少的情况下,每个连接池的响应时间差不多。是由于并发少,基本上没有资源竞争。
- 在并发较高的情况下,随着并发的升高,hikariCP响应时间基本上没有变动。
- c3p0随着并发的提高,性能急剧下降。
4、使用Druid优化工具类
1、需要添加一个druid的数据源jar包,例如druid-1.1.9.jar
2、修改工具类加入Druid数据源,代码编写:
- 创建druid数据源
- 给数据源赋值
- 建立连接
- 关闭连接
package com.gaj.utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.alibaba.druid.pool.DruidDataSource;
/**
* 加入Druid数据连接池的工具类
* @author Jan
*
*/
public class DruidJDBCUtil {
// 连接数据库的驱动类
private static String CONN_DIRVER = "com.mysql.jdbc.Driver";
// 连接数据库的url
private static String CONN_URL = "jdbc:mysql://localhost:3306/jdbcdb?characterEncoding=utf-8";
// 连接数据库的用户名
private static String CONN_USER = "root";
// 连接数据库的密码
private static String CONN_PASS = "root";
// 创建数据源对象
private static DruidDataSource dataSource = new DruidDataSource();
// 静态块中赋值
static{
dataSource.setDriverClassName(CONN_DIRVER);
dataSource.setUrl(CONN_URL);
dataSource.setUsername(CONN_USER);
dataSource.setPassword(CONN_PASS);
}
/**
* 从连接池里拿连接
* @return 连接
*/
public static Connection getConnction(){
Connection conn = null;
try {
// 之前给过连接属性了,这里就可以不用给了
conn = dataSource.getConnection();
} catch (SQLException e) {
System.out.println("连接数据库的四大属性可能不正确:" + e.getMessage());
e.printStackTrace();
}
return conn;
}
/**
* 资源释放
* @param conn 连接
* @param ps 预编译操作对象
* @param rs 结果集
*/
public static void closeAllConnection(Connection conn, PreparedStatement ps, ResultSet rs){
// 先开后关原则
if(null != rs){
try {
rs.close();
} catch (SQLException e) {
System.out.println("关闭数据结果集出错:" + e.getMessage());
e.printStackTrace();
}
}
if(null != ps){
try {
ps.close();
} catch (SQLException e) {
System.out.println("关闭数据库操作对象出错:" + e.getMessage());
e.printStackTrace();
}
}
if(null != conn){
try {
conn.close();
} catch (SQLException e) {
System.out.println("关闭数据库连接出错:" + e.getMessage());
e.printStackTrace();
}
}
}
}
5、使用Druid数据源后进行CURD
- CURD功能函数:
package com.gaj.function;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.gaj.entity.Person;
import com.gaj.utils.DruidJDBCUtil;
/**
* 优化版
* JDBC连接数据库的CURD操作
* 加入了Druid数据源连接池(只优化了JDBCUtil工具类)
* 这里JDBC代码没多大改动
* @author Jan
* @version 3.0
*
*/
public class DruidJDBCFunction {
// 新增
public static int insert(Person person) throws Exception{
// 从DruidJDBCUtil获取连接
Connection conn = DruidJDBCUtil.getConnction();
// 编写sql 占位符占位
String sql = "insert into person values(null,?,?,?,?)";
// 预编译操作数据库对象
PreparedStatement ps = conn.prepareStatement(sql);
// 占位符赋值
ps.setString(1, person.getPname());
ps.setString(2, person.getSex());
ps.setInt(3, person.getAge());
ps.setString(4, person.getFrom());
// 执行sql
int count = ps.executeUpdate();
// 释放
DruidJDBCUtil.closeAllConnection(conn, ps, null);
// 返回影响行数
return count;
}
// 删除
public static int delete(Person person) throws Exception{
// 获取连接
Connection conn = DruidJDBCUtil.getConnction();
// 编写sql
String sql = "delete from person where id = ?";
// 预编译 执行对象
PreparedStatement ps = conn.prepareStatement(sql);
// 占位符赋值
ps.setInt(1, person.getPid());
// 执行
int count = ps.executeUpdate();
// 释放
DruidJDBCUtil.closeAllConnection(conn, ps, null);
// 返回
return count;
}
// 修改
public static int update(Person person) throws Exception{
// 获取连接
Connection conn = DruidJDBCUtil.getConnction();
// 编写sql
String sql = "update person set name=?,sex=?,age=?,`from`=? where id=?";
// 预编译 操作对象
PreparedStatement ps = conn.prepareStatement(sql);
// 占位符赋值
ps.setString(1, person.getPname());
ps.setString(2, person.getSex());
ps.setInt(3, person.getAge());
ps.setString(4, person.getFrom());
ps.setInt(5, person.getPid());
// 执行
int count = ps.executeUpdate();
// 释放
DruidJDBCUtil.closeAllConnection(conn, ps, null);
// 返回
return count;
}
// 查询全部
public static List<Person> queryAll() throws Exception{
List<Person> list = new ArrayList<>();
Person person = null;
// 获取连接
Connection conn = DruidJDBCUtil.getConnction();
// 编写sql 数据库的字段名与类的属性不一致时,使用as进行重命名 获取得时候用的是重命名后的字段
String sql = "select `id` pid, `name` pname, `sex`, `age`, `from` from person";
// 预编译
PreparedStatement ps = conn.prepareStatement(sql);
// 执行sql
ResultSet rs = ps.executeQuery();
// 解析结果
while(rs.next()){
// 创建person对象
person = new Person();
// 给person对象赋值
person.setPid(rs.getInt("pid"));
person.setPname(rs.getString("pname"));
person.setSex(rs.getString("sex"));
person.setAge(rs.getInt("age"));
person.setFrom(rs.getString("from"));
// 将person放入list
list.add(person);
}
// 关闭连接
DruidJDBCUtil.closeAllConnection(conn, ps, rs);
// 返回集合
return list;
}
// 按ID查询
public static Person findPersonById(Integer id) throws Exception{
Person person = null;
// 获取连接
Connection conn = DruidJDBCUtil.getConnction();
// 编写sql
String sql = "select * from person where id = ?";
// 预编译 操作对象
PreparedStatement ps = conn.prepareStatement(sql);
// 占位符赋值
ps.setInt(1, id);
// 执行
ResultSet rs = ps.executeQuery();
// 解析结果集
if(rs.next()){
// 创建person对象
person = new Person();
// 给person对象赋值
person.setPid(rs.getInt("id"));
person.setPname(rs.getString("name"));
person.setSex(rs.getString("sex"));
person.setAge(rs.getInt("age"));
person.setFrom(rs.getString("from"));
}
// 释放
DruidJDBCUtil.closeAllConnection(conn, ps, rs);
// 返回
return person;
}
}
- CURD测试类:
package com.gaj.test;
import java.util.List;
import org.junit.Test;
import com.gaj.entity.Person;
import com.gaj.function.DruidJDBCFunction;
/**
* 测试类 测试DruidJDBCFunction的CURD操作
* @author Jan
*
*/
public class Test3 {
// 新增测试
@Test
public void insertTest() throws Exception{
Person person = new Person();
person.setPname("王五");
person.setSex("男");
person.setAge(23);
person.setFrom("广东省");
int count = DruidJDBCFunction.insert(person);
System.out.println(count > 0 ? "Insert Successed!" : "Insert Failed!");
}
// 删除测试
@Test
public void deleteTest() throws Exception{
Person person = DruidJDBCFunction.findPersonById(10);
if(person != null){
int count = DruidJDBCFunction.delete(person);
System.out.println(count > 0 ? "Delete Successed!" : "Delete Failed!");
}else{
System.out.println("The object is not exists!");
}
}
// 修改测试
@Test
public void updateTest() throws Exception{
Person person = DruidJDBCFunction.findPersonById(11);
if(person != null){
person.setPname("赵六");
person.setSex("女");
person.setAge(19);
person.setFrom("湖北省");
int count = DruidJDBCFunction.update(person);
System.out.println(count > 0 ? "Update Successed!" : "Update Failed!");
}else{
System.out.println("The object is not exists!");
}
}
// 查询全部测试
@Test
public void queryAll() throws Exception{
List<Person> list = DruidJDBCFunction.queryAll();
for (Person person : list) {
System.out.println(person);
}
}
// 按ID查询测试
@Test
public void findPersonById() throws Exception{
Person person = DruidJDBCFunction.findPersonById(19);
System.out.println(person);
}
}
二、DButils工具类
DBUtils是java编程中的数据库操作实用工具,小巧简单实用,用于简化JDBC代码的开发,位于apache commons组件下的一个成员,但需要数据源支持。
1、Dbutils功能介绍
- QueryRunner中提供对sql语句操作的API
- ResultSetHandler接口,用于定义查询操作后,封装不同的结果集
- DBUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法
2、QueryRunner类构造
QueryRunner核心类用户执行sql语句
QueryRunner类的构造,这里主要用到public QueryRunner(DataSource ds)
需要一个数据源,例如druid就可以。
/**
* Executes SQL queries with pluggable strategies for handling
* <code>ResultSet</code>s. This class is thread safe.
*
* @see ResultSetHandler
*/
public class QueryRunner extends AbstractQueryRunner {
/**
* Constructor for QueryRunner.
*/
public QueryRunner() {
super();
}
/**
* Constructor for QueryRunner that controls the use of <code>ParameterMetaData</code>.
*
* @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
* if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
* and if it breaks, we'll remember not to use it again.
*/
public QueryRunner(boolean pmdKnownBroken) {
super(pmdKnownBroken);
}
/**
* Constructor for QueryRunner that takes a <code>DataSource</code> to use.
*
* Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
* <code>DataSource</code>.
*
* @param ds The <code>DataSource</code> to retrieve connections from.
*/
public QueryRunner(DataSource ds) {
super(ds);
}
/**
* Constructor for QueryRunner that takes a <code>DataSource</code> and controls the use of <code>ParameterMetaData</code>.
* Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
* <code>DataSource</code>.
*
* @param ds The <code>DataSource</code> to retrieve connections from.
* @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
* if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
* and if it breaks, we'll remember not to use it again.
*/
public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
super(ds, pmdKnownBroken);
}
}
3、QueryRunner的主要方法
- update:执行更新数据,用于DML操作(增删改);
/**
* Executes the given INSERT, UPDATE, or DELETE SQL statement. The
* <code>Connection</code> is retrieved from the <code>DataSource</code>
* set in the constructor. This <code>Connection</code> must be in
* auto-commit mode or the update will not be saved.
*
* @param sql The SQL statement to execute.
* @param params Initializes the PreparedStatement's IN (i.e. '?')
* parameters.
* @throws SQLException if a database access error occurs
* @return The number of rows updated.
*/
public int update(String sql, Object... params) throws SQLException {
Connection conn = this.prepareConnection();
return this.update(conn, true, sql, params);
}
- query:查询数据,用于DQL操作。
/**
* Executes the given SELECT SQL query and returns a result object.
* The <code>Connection</code> is retrieved from the
* <code>DataSource</code> set in the constructor.
* @param <T> The type of object that the handler returns
* @param sql The SQL statement to execute.
* @param rsh The handler used to create the result object from
* the <code>ResultSet</code>.
* @param params Initialize the PreparedStatement's IN parameters with
* this array.
* @return An object generated by the handler.
* @throws SQLException if a database access error occurs
*/
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
Connection conn = this.prepareConnection();
return this.<T>query(conn, true, sql, rsh, params);
}
4、ResultSetHandler
- ResultSetHandler是数据集处理类,是一个接口。
/**
* Implementations of this interface convert ResultSets into other objects.
*
* @param <T> the target type the input ResultSet will be converted to.
*/
public interface ResultSetHandler<T> {
/**
* Turn the <code>ResultSet</code> into an Object.
*
* @param rs The <code>ResultSet</code> to handle. It has not been touched
* before being passed to this method.
*
* @return An Object initialized with <code>ResultSet</code> data. It is
* legal for implementations to return <code>null</code> if the
* <code>ResultSet</code> contained 0 rows.
*
* @throws SQLException if a database access error occurs
*/
T handle(ResultSet rs) throws SQLException;
}
- ResultSetHandler的子类
ResultSetHandler子类 | 说明 |
ArrayHandler | 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值 |
ArrayListHandler | 将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。 |
BeanHandler | 将结果集中第一条记录封装到一个指定的javaBean中。 |
BeanListHandler | 将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中 |
ColumnListHandler | 将结果集中指定的列的字段值,封装到一个List集合中 |
KeyedHandler | 将结果集中每一条记录封装到Map<String,Object>,在将这个map集合做为另一个Map的value,另一个Map集合的key是指定的字段的值。 |
MapHandler | 将结果集中第一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值 |
MapListHandler | 将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中。 |
ScalarHandler | 它是用于单个数据。例如select count(*) from 表操作。 |
5、使用DButils优化工具类
1、需要添加一个dbutils的jar包,例如commons-dbutils.jar
2、修改工具类加入Druid数据源,代码编写:
- 创建druid数据源
- 给数据源赋值
- 由于DButils的QueryRunenr执行类需要一个DataSource,因此只需要返回druid的数据源即可
package com.gaj.utils;
import com.alibaba.druid.pool.DruidDataSource;
/**
* 在加入Druid连接池的基础上增加了dbutils-1.6
* dbutils也只能获取连接池的连接
* @author Jan
*
*/
public class DruidDBUtil {
// 四大连接属性
// Driver类
private static final String CONN_DRIVER = "com.mysql.jdbc.Driver";
// 数据库地址 url
private static final String CONN_URL = "jdbc:mysql://localhost:3306/jdbcdb?characterEncoding=utf-8";
// 用户名
private static final String CONN_USER = "root";
// 密码
private static final String CONN_PASS = "root";
// 创建数据源对象
public static DruidDataSource dataSource = new DruidDataSource();
// 静态块赋值给druid数据源
static {
dataSource.setDriverClassName(CONN_DRIVER);
dataSource.setUrl(CONN_URL);
dataSource.setUsername(CONN_USER);
dataSource.setPassword(CONN_PASS);
}
/**
* DBUtils执行SQL语句需要传入一个DataSource
* 根据这一特性将自己的DruidJDBCUtil加以改造
* DBUtils有自己的连接和释放方式
* 这里只需要返回一个数据源就可以了
* 添加获取数据源的方法
* @return
*/
public static DruidDataSource getDataSource(){
return dataSource;
}
}
6、加入DButils后进行JDBC
- CURD功能函数:
package com.gaj.function;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import com.gaj.entity.Person;
import com.gaj.utils.DruidDBUtil;
/**
* 最终版
* JDBC连接数据库的CURD操作
* 在加入了Druid数据源连接池的基础上并使用了dbutils工具类
* 不需要手动写代码连接和释放了
* @author Jan
* @version 4.0
*
*/
public class DButilsJDBCFunction {
// 创建SQL执行对象
private static QueryRunner qr = new QueryRunner(DruidDBUtil.getDataSource());
// 新增
public static int insert(Person person) throws Exception{
// 1.编写sql语句
String sql = "insert into person values(null,?,?,?,?)";
// 2.占位符赋值
Object [] params = {person.getPname(), person.getSex(), person.getAge(), person.getFrom()};
// 3.执行sql
int count = qr.update(sql, params);
// 4.返回影响行数
return count;
}
// 删除
public static int delete(Person person) throws Exception{
// 1.编写sql
String sql = "delete from person where id = ?";
// 2.占位符赋值
Object[] params = {person.getPid()};
// 3.执行sql
int count = qr.update(sql, params);
// 4.返回
return count;
}
// 修改
public static int update(Person person) throws Exception{
// 1.编写sql
String sql = "update person set name=?,sex=?,age=?,`from`=? where id=?";
// 2.占位符赋值
Object[] params = {person.getPname(), person.getSex(), person.getAge(), person.getFrom(), person.getPid()};
// 3.执行sql
int count = qr.update(sql, params);
// 4.返回
return count;
}
// 查询全部
public static List<Person> queryAll() throws Exception{
// 1.编写sql
String sql = "select id pid, name pname, sex, age, `from` from person;";
// 2.执行sql
List<Person> list = qr.query(sql, new BeanListHandler<Person>(Person.class));
// 3.返回
return list;
}
// 按id查询
public static Person findPersonById(Integer id) throws Exception{
// 1.编写sql
String sql = "select id pid, name pname, sex, age, `from` from person where id=?;";
// 2.占位符赋值
Object [] params = {id};
// 3.执行sql
Person person = qr.query(sql, new BeanHandler<Person>(Person.class), params);
// 4.返回
return person;
}
}
知识点:BeanHandler的设置是分两步完成
- BeanHandler<Person>通过泛型设置了BeanHandler的类型
- 然后Person.class指定了这个BeanHandler应该返回Person类的对象
Person.class是获取Person的class对象。
- CURD测试类:
package com.gaj.test;
import java.util.List;
import org.junit.Test;
import com.gaj.entity.Person;
import com.gaj.function.DButilsJDBCFunction;
/**
* 测试类 测试DButilsJDBCFunction的CURD操作
* @author Jan
*
*/
public class Test4 {
// 新增
@Test
public void insertTest() throws Exception{
Person person = new Person();
person.setPname("赵闪闪");
person.setSex("男");
person.setAge(20);
person.setFrom("山西省");
int count = DButilsJDBCFunction.insert(person);
System.out.println(count > 0 ? "Insert Successed!" : "Insert Failed!");
}
// 删除
@Test
public void deleteTest() throws Exception{
Person person = DButilsJDBCFunction.findPersonById(12);
if(person != null){
int count = DButilsJDBCFunction.delete(person);
System.out.println(count > 0 ? "Delete Successed!" : "Delete Failed!");
}else{
System.out.println("The object is not exists!");
}
}
// 修改
@Test
public void updateTest() throws Exception{
Person person = DButilsJDBCFunction.findPersonById(13);
if(person != null){
person.setPname("张亮亮");
person.setSex("男");
person.setAge(21);
person.setFrom("陕西省");
int count = DButilsJDBCFunction.update(person);
System.out.println(count > 0 ? "Update Successed!" : "Update Failed!");
}else{
System.out.println("The object is not exists!");
}
}
// 查询全部
@Test
public void queryAllTest() throws Exception{
List<Person> list = DButilsJDBCFunction.queryAll();
for (Person person : list) {
System.out.println(person);
}
}
// 按ID查询
@Test
public void findPersonByIdTest() throws Exception{
Person person = DButilsJDBCFunction.findPersonById(11);
System.out.println(person);
}
}
三、分页查询
- 分页功能函数:
package com.gaj.function;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import com.gaj.entity.Person;
import com.gaj.utils.DruidDBUtil;
/**
* 分页功能需要的函数
* @author Jan
*
*/
public class PagingJDBCFunction {
// 创建sql执行对象
private static QueryRunner qr = new QueryRunner(DruidDBUtil.getDataSource());
// 添加
public static int insert(Person person) throws Exception{
// 1.编写sql
String sql = "insert into person values(null,?,?,?,?);";
// 2.占位符赋值
Object[] params = {person.getPname(), person.getSex(), person.getAge(),person.getFrom()};
// 3.执行sql
int count = qr.update(sql, params);
// 4.返回
return count;
}
// 删除
public static int delete(String name) throws Exception{
// 1.编写sql
String sql = "delete from person where name like ?";
// 2.占位符赋值
Object [] params = {name};
// 3.执行sql
int count = qr.update(sql, params);
// 4.返回
return count;
}
// 分页查询
public static List<Person> findPersonByPage(Integer startIndex, Integer pageSize) throws Exception{
// 1.编写sql
String sql = "select id pid, name pname, sex, age, `from` from person limit ?,?";
// 2.占位符赋值
Object[] params = {startIndex, pageSize};
// 3.执行sql
List<Person> list = qr.query(sql, new BeanListHandler<Person>(Person.class), params);
// 4.返回
return list;
}
// 总条数
public static int countPersonSum() throws Exception{
// 1.编写sql
String sql = "select count(1) from person;";
// 2.执行sql
// 错误:Long cannot be cast to Integer
// int count = qr.query(sql, new ScalarHandler<Integer>());
Number count = qr.query(sql, new ScalarHandler<Number>());
// 3.返回结果
return count.intValue();
}
}
知识点:new ScalarHandler/(),这里如果用Integer会报错,这里可以用Long的父类Number接收,再返回的时候转换成Integer即可。
- 分页的测试类:
package com.gaj.test;
import java.util.List;
import org.junit.Test;
import com.gaj.entity.Person;
import com.gaj.function.PagingJDBCFunction;
/**
* 分页查询
* @author Jan
*
*/
public class Test5 {
// 插入300条分页数据
@Test
public void InsertPagingData() throws Exception{
int count = 0;
Person person = null;
for (int i = 1; i <= 300; i++) {
person =new Person();
person.setPname("测试数据" + i);
person.setSex(i % 2 == 0 ?"男":"女");
person.setAge(i % 31 + 20);
person.setFrom("-");
count = PagingJDBCFunction.insert(person);
System.out.println("数据" + i + (count > 0 ? "Insert Succeed!": "Insert Failed!"));
}
}
// 删除测试数据
@Test
public void deleteBeatch() throws Exception{
int count = PagingJDBCFunction.delete("测试数据%");
System.out.println("被删除的记录数量:" + count);
}
/**
* 测试分页 每页10条数据
*
* 总条数 totalNum
* 总页数 totalPage
* 页容量 pageSize
* 起始位置 pageIndex:(行索引 - 1 ) * pageSize
* @throws Exception
*/
@Test
public void PagingTest() throws Exception{
int pageSize = 10;
int totalNum = PagingJDBCFunction.countPersonSum();
int totalPage = (totalNum % pageSize == 0) ? (totalNum / pageSize) : (totalNum / pageSize + 1);
int pageIndex;
List<Person> list = null;
for(int i = 1; i <= totalPage; i++){
pageIndex = (i - 1) * pageSize;
list = PagingJDBCFunction.findPersonByPage(pageIndex, pageSize);
System.out.println("总条数:" + totalNum + "\t总页数:" + totalPage + "\t页容量:" + pageSize + "\t当前页:" + i);
for (Person person : list) {
System.out.println(person);
}
}
}
}
四、事务
1、事务的概念
事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败
2、一个关于事务的例子
转账功能函数:
package com.gaj.function;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import com.gaj.utils.DruidDBUtil;
/**
* 事务处理
* @author Jan
*
*/
public class TransactionJDBCFunction {
// 创建执行对象
private static QueryRunner qr = new QueryRunner(DruidDBUtil.getDataSource());
public static void transfer(String from, String to, Double money) throws Exception{
// 1.编写sql 转出
String sql1 = "update account set money = money - ? where name = ?";
// 2.占位符赋值
Object[] params1 = {money, from};
// 看一下是否为同一个连接
System.out.println(DruidDBUtil.getDataSource().getConnection());
// 3.执行sql
int count1 = qr.update(sql1, params1);
// 故意引发一个错误
// int i = 1/0;
// 4.编写sql 转入
String sql2 = "update account set money = money + ? where name = ?";
// 5.占位符赋值
Object[] params2 = {money, to};
// 看一下是否为同一个连接
System.out.println(DruidDBUtil.getDataSource().getConnection());
// 6.执行sql
int count2 = qr.update(sql2, params2);
// 7.判断
if(count1 > 0 && count2 > 0){
System.out.println("转账成功!");
}else{
System.out.println("转账失败!");
}
}
}
- 转账问题测试类:
package com.gaj.test;
import org.junit.Test;
import com.gaj.function.TransactionJDBCFunction;
/**
* 事务测试类
* @author Jan
*
*/
public class Test6 {
@Test
public void transferTest() throws Exception{
// 李四 转100元给张三
// TransactionJDBCFunction.transfer("李四", "张三", 100D);
TransactionJDBCFunction.transfer0("李四", "张三", 100D);
}
@Test
public void show() throws Exception{
TransactionJDBCFunction.QueryAll();
}
}
不使用事务如果在两个sql语句中间报错,会由于使用两个不同的连接,一个成功一个失败:
- 使用事务:
Connection对象的方法名 | 描述 |
conn.setAutoCommit(false) | 开启事务 |
conn.commit() | 提交事务 |
conn.rollback() | 回滚事务 |
- 编写事务的步骤:
- 从数据库获取了一个数据源
- 将数据源的事务自动提交设置为false
- 每次执行SQL都用同一个Connection连接
- 在最后一条执行sql语句后使用conn.commit()提交,保证所有sql语句都执行成功
- 在catch块中使用conn.rollback()回滚
public static void transfer0(String from, String to, Double money) throws Exception {
// 从数据源获取一个连接
Connection conn = DruidDBUtil.getDataSource().getConnection();
// 设置事务的手动提交
conn.setAutoCommit(false);
try {
// 1.编写sql 转出
String sql1 = "update account set money = money - ? where name = ?";
// 2.占位符赋值
Object[] params1 = {money, from};
// 3.执行sql
int count1 = qr.update(conn, sql1, params1);
// 故意引发一个错误
int i = 1/0;
// 4.编写sql 转入
String sql2 = "update account set money = money + ? where name = ?";
// 5.占位符赋值
Object[] params2 = {money, to};
// 6.执行sql
int count2 = qr.update(conn, sql2, params2);
// 没有报错 提交
conn.commit();
// 7.判断
if(count1 > 0 && count2 > 0){
System.out.println("转账成功!");
}else{
System.out.println("转账失败!");
}
} catch (Exception e) {
// 报错 回滚
conn.rollback();
conn.close();
e.printStackTrace();
}
}
public static void QueryAll() throws Exception{
String sql = "select * from account";
qr.query(sql, new ResultSetHandler<Object>() {
@Override
public Object handle(ResultSet rs) throws SQLException {
while(rs.next()){
System.out.println(rs.getInt(1) +"\t"+ rs.getString(2) +"\t"+ rs.getDouble(3));
}
return null;
}
});
}