DbUtils工具和连接池
一,DbUtils认知
- 什么的DuBtils
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
通俗来说:DbUtils就是JDBC开发的工具包,完成对原生JDBC操作的封装! - DbUtils中的核心对象
(1)QueryRunner类:替换了原生JDBC中的Statement以及PreparedStatement,支持使用?绑定动态数据,可以有效防止SQL注入!
(2)ResultSetHandler接口:此接口下,提供了八大实现类,用于针对各种查询结果集提供封装方案!!!
(3)DbUtils类:关闭资源
二,DbUtils的数据库操作
既然DbUtils是第三方工具,简化了jdbc开发,所以使用前需要导包
这个只是功能代码封装的jar,想要操作数据库,必须导入驱动,所以昨天的mysql-connector依旧需要导入!这两个缺一不可。
它极大的提高了查找的效率
- DbUtils操作数据库的步骤
(1)注册驱动
(2)连接数据库
(3)创建QueryRunner对象(没有DbUtils的时候是获取编译器)
(4)执行sql语句‘
(5)处理结果
(6)关闭资源 - 增删改
//DbUtils的添加操作
@Test
public void insertDemo() throws ClassNotFoundException, SQLException {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///day03", "root", "123");
//3、创建QueryRunner对象
QueryRunner qRunner = new QueryRunner();
//4、执行sql语句
/*
增删改的sql操作,QueryRunner对象,调用update()方法,返回值为int整型数字,代表影响的数据行数
update(连接对象Connection, sql语句, Object类型的可变参数)
可变参数的赋值方式:
1、将参数挨个书写出来,以逗号隔开!与值的个数要一致,?与值的类型要一致
2、给数组,将为?赋值的数据放在数组中,直接传数组
*/
/*int row = qRunner.update(connection, "insert into student values (null, ?, ?, ?)",
"霍金", "男", "预言");*/
Object[] objs = {"臭棋篓子", "男", "下棋"};
int row = qRunner.update(connection, "insert into student values (null, ?, ?, ?)", objs);
//5、处理结果
System.out.println(row > 0 ? "添加成功" : "添加失败");
//6、释放资源
DbUtils.close(connection);
}
@Test
public void updateDemo() {
Connection connection = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql:///day03", "root", "123");
//3、创建QueryRunner对象
QueryRunner qRunner = new QueryRunner();
//4、执行sql语句
int row = qRunner.update(connection, "update student set hobby = '下臭棋' where sid = ?", 9);
//5、处理结果
System.out.println(row > 0 ? "修改成功" : "修改失败");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
//6、释放资源
DbUtils.close(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- 查询
(1)查询结果的种类
多行多列 select * from student;
单行单列 select count(*) from student;
单行多列 select * from student where sid = 1;
单列多行 select sname from student;
(2)在DbUtils中,针对任意查询结果集,都提供了对应的封装方案!DbUtils中有个查询结果集接口ResultSetHandler,这个接口下,有八大实现类!
八个结果集对象,到底使用哪一个来接收本次查询的结果,取决于你的需求,你书写的sql语句!
如果获取的是表中的单行、单条记录(单行多列):BeanHandler
如果获取的是表中的多行、多条记录(多行多列):BeanListHandler
如果获取的是单一的数据(单行单列):ScalarHandler
如果获取的是某一个字段的所有值(单列多行):ColumnListHandler
需求 -> sql语句 -> 查询的结果集形式 -> 选用哪一个封装方案
(3)实体类Student代码:
/*
数据库中的表,在程序中我们可以编写实体类来进行映射,并存储数据!
1、数据库中的表就是创建的实体类
2、表中的字段就是类中的属性
字段名称与属性名称对应
字段类型与属性类型一致
3、表中的一条记录就是类创建的一个对象
JavaBean是实体类的设计规范,它要求类:
1、由public修饰,公开
2、属性私有化
3、为私有属性提供setter、getter方法
4、必须保证无参构造的存在
*/
public class Student {
private Integer sid;
private String sname;
private String gender;
private String hobby;
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public Student() {
}
public Student(Integer sid, String sname, String gender, String hobby) {
this.sid = sid;
this.sname = sname;
this.gender = gender;
this.hobby = hobby;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", gender='" + gender + '\'' +
", hobby='" + hobby + '\'' +
'}';
}
}
(4)案例代码
/*
BeanHandler 将结果集中的第一条记录封装到一个指定的JavaBean中!
单行多列时,使用BeanHandler接收!
*/
@Test
public void beanHandlerDemo() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection connection =
DriverManager.getConnection("jdbc:mysql:///day03", "root", "123");
QueryRunner qRunner = new QueryRunner();
/*
多态的实现方式:
1、继承多态 父类引用指向子类对象
Animal父类 子类Dog Cat
Animal animal = new Dog();
Animal animal = new Cat();
2、接口多态 接口的引用指向了实现了该接口类的对象
List list = new ArrayList();
List list = new LinkedList();
如果现在要通过QueryRunner执行查询sql语句,需要调用query()方法
Student.class 该类的字节码对象传递,做得事情:
1、找到了无参构造创建对象,所以说实体类中必须存在无参构造!
没有无参构造,直接崩溃,创建不了对象!
2、找到了对应属性的setter方法,完成了赋值!
为了保证赋值的成功,属性名必须与表中字段名一致!
*/
Student student = qRunner.query(connection, "select * from student where sid = ?",
new BeanHandler<Student>(Student.class), 1);
System.out.println(student);
DbUtils.close(connection);
}
/*
BeanListHandler 将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中
多行多列 选用BeanListHandler
*/
@Test
public void beanListHandlerDemo() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection connection =
DriverManager.getConnection("jdbc:mysql:///day03", "root", "123");
QueryRunner qRunner = new QueryRunner();
List<Student> stus =
qRunner.query(connection, "select * from student", new BeanListHandler<Student>(Student.class));
for (Student student : stus) {
System.out.println(student);
}
DbUtils.close(connection);
}
/*
ScalarHandler 用于封装单一数据
单行单列,选用ScalarHandler,默认返回值Object类型,可以向下转型!
*/
@Test
public void scalarHandlerDemo() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection connection =
DriverManager.getConnection("jdbc:mysql:///day03", "root", "123");
QueryRunner qRunner = new QueryRunner();
Long count = (Long)qRunner.query(connection, "select count(*) from student", new ScalarHandler());
System.out.println("学生的数量为:" + count);
DbUtils.close(connection);
}
/*
ColumnListHandler 将结果集中指定列的数据封装到List集合中!
单列多行,选用ColumnListHandler
*/
@Test
public void columnListHandlerDemo() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection connection =
DriverManager.getConnection("jdbc:mysql:///day03", "root", "123");
QueryRunner qRunner = new QueryRunner();
List<Object> objs = qRunner.query(connection, "select * from student",
new ColumnListHandler("hobby"));
for (Object obj : objs) {
System.out.println(obj);
}
/*List<Object> objs = qRunner.query(connection, "select sname from student", new ColumnListHandler());
for (Object obj : objs) {
System.out.println(obj);
}*/
DbUtils.close(connection);
}
三, 连接池
- 什么要使用连接池(连接池的作用)
数据库连接对象是一种昂贵的资源,目前我们书写的程序每发出一个请求都要创建一个数据库连接对象,用完直接关闭不能重复使用!关闭资源需要手动调用方法,如果一旦忘记会造成内存的溢出。当请求过于频繁时或者说数据库访问并发量过大时,很容易就造成内存的崩溃!为了解决这个数据库连接对象不能复用的问题,数据库连接池技术就出现了! - 什么是数据库连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。 - 数据库连接池的工作原理(三部分组成)
(1)连接池的建立
连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象(初始化的连接对象),以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。
(2)连接池中连接的使用管理
连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:
当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。
当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。
(3)连接池的关闭
连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。
(4) 数据库连接时配置的参数(可选参数,不是必须配置的,有默认值)
最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
最大空闲时间:连接池对象最长的闲置时间,如果长时间没有被使用,那么此连接对象就会被销毁。
获取连接超时时间:池子中总共有50条连接,当这50条连接都处于工作状态时,第51个连接请求到达,让其加入到等待队列中,等待时间如果超过了设置的超时时间,则抛出异常
初始化连接数:当数据库连接池创建时,初始化的连接数量
(5)数据库连接池必须的配置参数
driverClass 驱动类名
url 访问数据库的信息
username 访问MySQL的用户名
password 访问MySQL的密码
我们只需要给池子配置这些参数,那么其中管理的所有的数据库连接对象,都具备了这些参数,就知道了数据库连接时的细节!
连接池技术(dbcp,c3p0,druid)
不同的连接池技术,但是本质要实现的功能都是一致的,就是为了管理数据库连接对象,实现复用!只是不同的技术对于数据库连接对象的管理方式略有区别,数据库连接池使用容器、数据结构有所区别!
我们想要在程序内部使用数据库连接池,我们就需要使用一个对象来表示,这个对象称为数据源对象DataSource!只不过不同的数据连接池技术,在程序获取数据源的代码不一致,但是终归都要拿到这个DataSource
- c3po连接池(代码角度最少,性能角度最低)
1.1 背景
C3P0是我使用的第一款数据库连接池,在很长一段时间内,它一直是Java领域内数据库连接池的代名词,当年盛极一时的Hibernate都将其作为内置的数据库连接池,可以业内对它的稳定性还是认可的。C3P0功能简单易用,稳定性好这是它的优点,但是性能上的缺点却让它彻底被打入冷宫。C3P0的性能很差,差到即便是同时代的产品相比它也是垫底的,更不用和Druid、HikariCP等相比了。正常来讲,有问题很正常,改就是了,但c3p0最致命的问题就是架构设计过于复杂,让重构变成了一项不可能完成的任务。随着国内互联网大潮的涌起,性能有硬伤的c3p0彻底的退出了历史舞台。
1.2 导包
1.3 配置文件
放在src下面
取名叫c3p0.properties
1.4 书写c3p0数据源DataSource获取的工具类
如果c3p0的配置文件,名称为c3p0.properties
并且这个文件又放在了src根目录下面
那么数据源在获取时,就会自动读取配置文件中的信息
/**
该类的作用是获取C3P0数据库连接池对应的数据源对象!
*/
public class C3P0Utils {
private static DataSource dataSource = null;
/*
如果c3p0的配置文件名称为c3p0.properties
而且将其放在了src的根目录下面
那么就会自动加载配置文件中的信息,来初始化c3p0的数据源对象!
*/
static {
dataSource = new ComboPooledDataSource();
}
public static DataSource getDataSource() {
return dataSource;
}
}
1.5DbUtils+c0p3完成数据库操作
//使用c3p0数据库连接池 + DbUtils 完成数据库操作
@Test
public void deleteDemo() throws SQLException {
//1、手动从数据源中获取连接,操作数据
Connection connection = C3P0Utils.getDataSource().getConnection();
QueryRunner qRunner = new QueryRunner();
int row = qRunner.update(connection, "delete from student where sid = ?", 11);
System.out.println(row > 0 ? "删除成功" : "删除失败");
connection.close();
}
@Test
public void selectDemo() throws SQLException {
//2、创建QueryRunner时指定数据源,自动获取连接
QueryRunner qRunner = new QueryRunner(C3P0Utils.getDataSource());
List<Student> stus = qRunner.query("select * from student", new BeanListHandler<Student>(Student.class));
for (Student student : stus) {
System.out.println(student);
}
}
- 数据库连接池(druid)
1.1背景
Druid简介:
Druid是阿里开源的数据库连接池,作为后起之秀,性能比dbcp、c3p0更高,使用也越来越广泛。
当然Druid不仅仅是一个连接池,还有很多其他的功能。
Druid的优点:
高性能。性能比dbcp、c3p0高很多。
只要是jdbc支持的数据库,druid都支持,对数据库的支持性好。并且Druid针对oracle、mysql做了特别优化。
提供监控功能。可以监控sql语句的执行时间、ResultSet持有时间、返回行数、更新行数、错误次数、错误堆栈等信息,来了解连接池、sql语句的工作情况,方便统计、分析SQL的执行性能
1.2 导包一个
1.3配置文件(druid)
1.4 书写druid数据源获取的工具类
该类的作用只有一个:获取Druid提供出来的数据源对象
任何连接池技术的使用,都需要拿到其对应的数据源对象!
拿到了数据源对象,我们就可以从该对象中获取数据库连接了!
工具类的几个注意事项
1、属性尽量私有化,不要让外界直接修改
2、方法通常都是静态方法
3、如果一旦产生了异常,采用try catch,不要抛出
public class DruidUtils {
private static DataSource dataSource=null;
static{
InputStream inputStream=null;
try {
//创建一个属性集对象
Properties properties= new Properties();
//创建一个输入流对象,去读写配置文件
inputStream = new FileInputStream(new File("src/druid.properties"));
//把输入流读取到的对象加载到属性集中
properties.load(inputStream);
//将属性集对象传入到druid创建数据源的方法中,将保存的配置信息给数据源对象
dataSource=DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(inputStream!=null){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//获取数据源的方法
public static DataSource getDataSource(){
return dataSource;
}
}
1.5 使用DbUtils+druid完成数据库的操作
(1) 使用DbUtils + druid完成数据库操作的方式1:
1、从数据源中获取连接对象
2、创建QueryRunner对象
3、执行sql语句
4、处理结果
5、释放资源
public class DruidTest {
@Test
public void insertDemo() throws SQLException {
//手动获取数据库连接
Connection connection = DruidUtils.getDataSource().getConnection();
QueryRunner queryRunner = new QueryRunner();
int update = queryRunner.update(connection, "insert into stu values(?,?,?,?,?)", "张二麻子", 18, 155, 1000, "溜老虎");
System.out.println(update>0?"添加成功":"添加失败");
//把连接还给连接池
DbUtils.close(connection);
}
(2) 使用DbUtils + druid完成数据库操作的方式2:
1、直接创建QueryRunner对象,并指定数据源,那么就会自动从数据源中获取连接对象
2、执行sql语句
3、处理结果
public void selectDemo() throws SQLException {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
Stu stu = queryRunner.query("select *from stu where sname=?", new BeanHandler<Stu>(Stu.class), "大娃");
System.out.println(stu );
}
总结两种方式:
1、手动从数据源中获取连接
2、自动从数据源中获取连接
选用哪一种,取决于要不要对连接对象进行操作,比如要进行事务操作,那就只能手动从数据源中拿到连接
如果不需要对连接对象进行操作,那就自动获取连接
以后数据库进行的操作
DbUtils+C3P0连接池完成数据库操作
(1)、导包
mysql驱动、dbutils、c3p0、mchange 四个jar包
(2)、在src根目录下面导入c3p0.properties配置文件,将文件中的信息改为正确的连接信息
(3)、导入C3P0Utils的工具类,得到数据源
(4)、快乐的写代码
手动从数据源中获取连接对象
自动从数据源中获取连接对象