1. 连接池
1.1 连接池概述
1.1.1 什么是连接池
连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。
1.1.2 为什么要学习连接池
连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。
从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)
1.2 自定义连接池
1.2.1 自定义连接池的步骤
① 编写一个类实现DataSource接口
② 重写getConnection方法
③ 初始化多个连接在内存中
④ 编写归还连接的方法
1.2.2 自定义连接池的代码实现
public class MyDataSource implements DataSource {
//将一些连接存入到内存中,可以定义一个集合,用于存储连接对象。
private List<Connection> connList = new ArrayList<Connection>();
//在初始化的时候提供一些连接
public MyDataSource(){
//初始化连接:
for(int i=1;i<=3;i++){
//向集合中存入连接
connList.add(JDBCUtils.getConnection());
}
}
//从连接池中获得连接的方法
@Override
public Connection getConnection() throws SQLException {
Connection conn = connList.remove(0); //为了防止同一连接被多次获取,所以使用remove方法将其暂时移出连接集合
return conn;
}
//编写一个归还连接的方法:
public void addBack(Connection conn){
connList.add(conn);
}
1.2.3 从连接池中获取连接代码实现
public static void dataSource(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
MyDataSource dataSource = null;
try{
//从连接池中获得连接:
dataSource = new MyDataSource();
conn = dataSource.getConnection();
//编写SQL:
String sql = "select * from emp";
//预编译SQL:
pstmt = conn.prepareStatement(sql);
//执行sql:
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println("name:" + rs.getString("name")+" salary:" +rs.getInt("salary"));
}
}catch (Exception e){
e.printStackTrace();
}finally {
// JDBCUtils.release(pstmt,conn,rs);
//此时就不能简单的通过close简单的销毁连接了
dataSource.addBack(conn);
if(rs!= null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(pstmt!=null){
try {
pstmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
1.3 自定义连接池的问题
1.3.1 使用接口的实现类完成的构造
MyDataSource dataSource = new MyDataSource();
这种方法不方便程序的扩展。
1.3.2 额外提供了方法归还连接
dataSource.addBack(conn);
这种方法增加使用连接池的用户的难度。
1.3.3 自定义连接池的问题解决
如果不提供自定义的方法就可以解决这个问题,但是连接要如何归还到连接池呢?
原来在Connection中是有一个close方法的,close方法完成了连接的销毁。能不能做一个事情,将原有的连接的close方法改为归还。
- 现在要做的事情就是将原有的close方法的逻辑改为归还。(增强一个类中的方法)。
- 如何增强一个类中的方法
① 采用继承的方式。(这种方式是最简单的,但是是由使用条件的:必须能够控制这个类的构造!)
② 采用装饰者模式。(要求增强的类和被增强的类实现相同的接口,在增强的类中获得被增强的类的引用)
③ 动态代理的方式。
装饰者模式:
interface Waiter{
public void server();
}
public class Waitress implements Waiter{
public void server(){
System.out.println("服务中");
}
}
public class WaitressWrapper implements Waiter{
private Waiter waiter;
public WaitressWrapper(Waiter waiter){
this.waiter = waiter;
}
public void server(){
System.out.println("微笑");
waiter.server();
}
}
1.3.4 解决代码实现
使用装饰者模式增强Connection中的close方法
思路及具体操作流程:
由于要增强close方法,所以创建一个MyConnectionWrapper类来实现Connection接口,但是实现Connection接口需要重写Connection中所有方法(三四十个),这任务量太大了,所以我们再创建一个ConnectionWrapper来作为模板类实现Connection方法,这个ConnectionWrapper内重写了所有Connection中的所有方法,然后再用MyConnectionWrapper继承ConnectionWrapper即可。
PS:这里👴没有搞到那个模板类
继承ConnectionWrapper之后,就可以再MyConnectionWrapper中单独对close方法进行重写:
public class MyConnectionWrapper extends ConnectionWrapper {
private Connection conn;
private List<Connection> connList;
public MyConnectionWrapper(Connection conn,List<Connection> connList) {
super();
this.conn = conn;
this.connList = connList;
}
@Override
public void close() throws SQLException {
connList.add(conn);
}
}
之后再去修改 连接池类(MyDataSource)中的获得连接方法即可:
@Override
public Connection getConnection() throws SQLException {
Connection conn = connList.remove(0); //为了防止同一连接被多次获取,所以使用remove方法将其暂时移出连接集合
MyConnectionWrapper connWrapper = new MyConnectionWrapper(conn,connList);
return connWrapper;
}
2.开源连接池的使用
2.1 Druid
2.1.1 Druid的概述
Druid为阿里旗下开源连接池产品,使用非常简单,可以与Spring框架进行快速整合。
2.1.2 手动配置完成连接池的使用
public static void demo01(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
//使用连接池获得连接:
// conn = JDBCUtils.getConnection();
DruidDataSource dataSource = new DruidDataSource();
//手动设置数据库连接的参数:
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///emp");
dataSource.setUsername("root");
dataSource.setPassword("266531");
conn = dataSource.getConnection();
String sql = "select * from emp";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println("name:" + rs.getString("name")+" salary:" +rs.getInt("salary"));
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(pstmt,conn,rs);
}
}
2.1.3 使用配置方式完成连接池的使用
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///emp
username=root
password=266531
其中配置文件里不仅可以写路径设置,用户名密码,还可以有
初始化连接:
initialSize=10
最大连接数量:
maxActive=50
最大空闲连接
maxIdle=20
最小空闲连接
minIdle=5
超时等待时间以毫秒为单位
maxWait=60000
public static void demo02(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
DataSource dataSource = null;
try{
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc03_druid/src/druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(prop);
conn = dataSource.getConnection();
String sql = "select * from emp";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println("name:" + rs.getString("name")+" salary:" +rs.getInt("salary"));
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(pstmt,conn,rs);
}
}
2.2 C3P0
2.2.1 C3P0的概述
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。
2.2.2 手动配置完成连接池的使用
注意,使用c3p0需要导入两个包,不然会报错。
public static void main(String[] args) {
//手动设置参数方式:
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//从连接池中获得连接
ComboPooledDataSource ds = new ComboPooledDataSource();
//设置连接参数:
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql:///emp");
ds.setUser("root");
ds.setPassword("266531");
conn = ds.getConnection();
String sql = "select * from emp";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
System.out.println("name:" + rs.getString("name") + " salary:" + rs.getInt("salary"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(pstmt, conn, rs);
}
}
2.2.3 使用配置方式完成连接池的使用
c3p0的连接池配置文件可以使用properties,但最好还是使用xml配置文件
配置文件一定要命名为c3p0-config
<c3p0-config>
<default-config> //这个是默认配置
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///emp</property>
<property name="user">root</property>
<property name="password">266531</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0"> //这个是其他配置,具体怎么选择在于创建ComboPooledDataSource对象时传递的参数是什么
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///emp</property>
<property name="user">root</property>
<property name="password">266531</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</named-config>
</c3p0-config>
public static void demo04(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//从连接池中获得连接
ComboPooledDataSource ds = new ComboPooledDataSource();
//在括号中传递对应参数可以选择xml中的不同配置,比如传递otherc3p0,就会使用下面的那个配置
//不给参数或者给错参数则会使用默认配置
conn = ds.getConnection();
String sql = "select * from emp";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
System.out.println("name:" + rs.getString("name") + " salary:" + rs.getInt("salary"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(pstmt, conn, rs);
}
}
2.3 C3p0
在 以上虽然成功使用了第三方数据库,但是每使用一次就要创建一个 ComboPooledDataSource 容器对象,这对资源无疑是巨大浪费,所以我们要优化工具类
//创建一个连接池:但是这个连接池只创建一次即可。
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();
public static Connection getConnection() throws Exception{
return dataSource.getConnection();
}
//获得连接池:
public static DataSource getDataSource(){
return dataSource;
}
释放资源方法的改进后面再进行。
3.DBUtils
3.1 DBUtils的概述
3.1.1 什么是DBUtils
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
是对JDBC的简单封装,而且没有影响性能。
3.1.2 为什么要学习DBUtils
因为JDBC手写比较麻烦,而且有很多代码是类似的。比如获得连接,预编译SQL,释放资源等。那么可以把这些代码抽出来放到工具类中。将类似的代码进行抽取。大大简化JDBC的编程。
3.2 DBUtils的API
3.2.1 DBUtils的API概述
3.2.1.1 QueryRunner对象
是DBUtils的一个核心运行类
构造方法:
方法:
在一般情况下,如果执行CRUD的操作:
构造:
QueryRunner(DataSource ds);
方法:
int update(String sql,Object... args);
T query(String sql,ResultSetHandler rsh,Object.. args);
如果有事务管理的话使用另一套完成CRUD的操作
构造:
QueryRunner();
方法:
int update(Connection conn,String sql,Object... args);
T query(Connection conn,String sql,ResultSetHandler rsh,Object... args);
3.2.1.2 DbUtils
Quietly意思是把异常也处理了。
3.3 DBUtils的使用
3.3.1 DBUtils的增删改操作
① DBUtils的添加操作
public static void demo01() throws Exception {
//创建核心类:QueryRunner:
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
qr.update("insert into remainder values(?,?)", "Spike", 5000000);
}
② DBUtils的修改操作
public static void demo02() throws Exception{
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
qr.update("update remainder set money=? where name=?",5000,"Spike");
}
③ DBUtils的删除操作
public static void demo03() throws Exception{
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
qr.update("delete from remainder where name = ?","Spike");
}
3.3.2 DBUtils的查询操作
查询代码实现
//查询一条记录
public static void demo04() throws Exception{
//创建核心类:
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
//执行查询
Account emp = qr.query("select * from remainder where name=?", new ResultSetHandler<Account>() {
@Override
public Account handle(ResultSet rs) throws SQLException {
Account account = new Account();
while (rs.next()) {
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
}
return account;
}
}, "Rarity");
System.out.println(emp);
}
//查询多条记录
public static void demo05() throws Exception{
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
List<Account> accounts = qr.query("select * from remainder", new ResultSetHandler<List<Account>>() {
@Override
public List<Account> handle(ResultSet rs) throws SQLException {
List<Account> accountList = new ArrayList<Account>();
Account account = new Account();
while (rs.next()) {
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
accountList.add(account);
}
return accountList;
}
});
System.out.println(accounts);
}
3.3.3 DBUtils的使用之ResultSetHandler的实现类
3.3.3.1 ArrayHandler
将一条
记录封装到一个数组当中。这个数组应该是Object[]。
public static void demo01() throws Exception{
//ArrayHandler:将一条记录封装到一个Object数组中
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
Object[] objects = qr.query("select * from remainder where name=?", new ArrayHandler(), "Rarity");
System.out.println(Arrays.toString(objects));
}
3.3.3.2 ArrayListHandler
将多条
记录封装到一个装有Object[]的List集合中。
public static void demo02() throws Exception{
//ArrayListHandler:将多条记录封装到一个装有Object数组的List集合中
//一条记录封装到Object[]数组中,多条记录就是多个Object[],那么多个Object数组就将其装入List集合中即可。
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
List<Object[]> remainders = qr.query("select * from remainder", new ArrayListHandler());
for(Object[] objects:remainders){
System.out.println(Arrays.toString(objects));
}
}
3.3.3.3 BeanHandler
将一条记录封装到一个JavaBean中。
public static void demo01() throws Exception{
//BeanHandler:将一条记录封装到一个JavaBean中
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
Account account = qr.query("select * from remainder where name = ?", new BeanHandler<Account>(Account.class), "Rarity");
System.out.println(account);
}
3.3.3.4 BeanListHandler
将多条记录封装到一个装有JavaBean的List集合中。
public static void demo02() throws Exception {
//BeanListHandler:将多条记录封装到一个装有JavaBean的List集合中
//一条记录就是一个JavaBean的对象,将多个JavaBean对象装到一个List集合中
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
List<Account> accountList = qr.query("select * from remainder", new BeanListHandler<Account>(Account.class));
for(Account account : accountList){
System.out.println(account);
}
}
3.3.3.5 MapHandler
将一条记录封装到一个Map集合中,Map的Key是列名,value是表中列的记录值。
public static void demo01() throws Exception {
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
Map<String, Object> objectMap = qr.query("select * from remainder where name = ?", new MapHandler(), "Rarity");
System.out.println(objectMap);
}
3.3.3.6 MapListHandler
将多条记录封装到一个装有Map的List集合中。
public static void demo02() throws Exception{
QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
//为什么是Map<String,Object> 因为列名一定是String,但是value则有int,有String,有date,所以使用object
List<Map<String, Object>> mapList = qr.query("select * from remainder", new MapListHandler());
for(Map<String,Object> m: mapList){
System.out.println(m);
}
}
3.3.3.7 ColumnListHandler
将数据中的某列的值封装到List集合中
3.3.3.8 ScalarHandler
将单个值封装。
3.3.3.9 KeyedHandler
将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的。