百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

JDBC第四篇「数据库连接池、DbUtils框架、分页」(修订版)

sinye56 2024-10-07 14:29 11 浏览 0 评论


1.数据库连接池

什么是数据库连接池

简单来说:数据库连接池就是提供连接的。。。

为什么我们要使用数据库连接池

  • 数据库的连接的建立和关闭是非常消耗资源的
  • 频繁地打开、关闭连接造成系统性能低下


编写连接池

  1. 编写连接池需实现java.sql.DataSource接口
  2. 创建批量的Connection用LinkedList保存【既然是个池,当然用集合保存、、LinkedList底层是链表,对增删性能较好】
  3. 实现getConnetion(),让getConnection()每次调用,都是在LinkedList中取一个Connection返回给用户
  4. 调用Connection.close()方法,Connction返回给LinkedList
private static LinkedList<Connection> list = new LinkedList<>();
//获取连接只需要一次就够了,所以用static代码块
static {
 //读取文件配置
 InputStream inputStream = Demo1.class.getClassLoader().getResourceAsStream("db.properties");
 Properties properties = new Properties();
 try {
 properties.load(inputStream);
 String url = properties.getProperty("url");
 String username = properties.getProperty("username");
 String driver = properties.getProperty("driver");
 String password = properties.getProperty("password");
 //加载驱动
 Class.forName(driver);
 //获取多个连接,保存在LinkedList集合中
 for (int i = 0; i < 10; i++) {
 Connection connection = DriverManager.getConnection(url, username, password);
 list.add(connection);
 }
 } catch (IOException e) {
 e.printStackTrace();
 } catch (ClassNotFoundException e) {
 e.printStackTrace();
 } catch (SQLException e) {
 e.printStackTrace();
 }
}
//重写Connection方法,用户获取连接应该从LinkedList中给他
@Override
public Connection getConnection() throws SQLException {
 System.out.println(list.size());
 System.out.println(list);
 //先判断LinkedList是否存在连接
 return list.size() > 0 ? list.removeFirst() : null; 
}

我们已经完成前三步了,现在问题来了。我们调用Conncetion.close()方法,是把数据库的物理连接关掉,而不是返回给LinkedList的

解决思路:

  1. 写一个Connection子类,覆盖close()方法
  2. 写一个Connection包装类,增强close()方法
  3. 用动态代理,返回一个代理对象出去,拦截close()方法的调用,对close()增强

分析第一个思路:

  • Connection是通过数据库驱动加载的,保存了数据的信息。写一个子类Connection,new出对象,子类的Connction无法直接继承父类的数据信息,也就是说子类的Connection是无法连接数据库的,更别谈覆盖close()方法了。


分析第二个思路:

  • 写一个Connection包装类。
  1. 写一个类,实现与被增强对象的相同接口【Connection接口】
  2. 定义一个变量,指向被增强的对象
  3. 定义构造方法,接收被增强对象
  4. 覆盖想增强的方法
  5. 对于不想增强的方法,直接调用被增强对象的方法
  • 这个思路本身是没什么毛病的,就是实现接口时,方法太多了!,所以我们也不使用此方法

分析第三个思路代码实现:

@Override
public Connection getConnection() throws SQLException {
 if (list.size() > 0) {
 final Connection connection = list.removeFirst();
 //看看池的大小
 System.out.println(list.size());
 //返回一个动态代理对象
 return (Connection) Proxy.newProxyInstance(Demo1.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() {
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 //如果不是调用close方法,就按照正常的来调用
 if (!method.getName().equals("close")) {
 method.invoke(connection, args);
 } else {
 //进到这里来,说明调用的是close方法
 list.add(connection);
 //再看看池的大小
 System.out.println(list.size());
 }
 return null;
 }
 });
 }
 return null;
}

我们上面已经能够简单编写一个线程池了。下面我们来使用一下开源数据库连接池

DBCP

使用DBCP数据源的步骤:

  1. 导入两个jar包【Commons-dbcp.jar和Commons-pool.jar】
  2. 读取配置文件
  3. 获取BasicDataSourceFactory对象
  4. 创建DataSource对象
private static DataSource dataSource = null;
static {
 try {
 //读取配置文件
 InputStream inputStream = Demo3.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
 Properties properties = new Properties();
 properties.load(inputStream);
 //获取工厂对象
 BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory();
 dataSource = basicDataSourceFactory.createDataSource(properties);
 } catch (IOException e) {
 e.printStackTrace();
 } catch (Exception e) {
 e.printStackTrace();
 }
}
public static Connection getConnection() throws SQLException {
 return dataSource.getConnection();
}
//这里释放资源不是把数据库的物理连接释放了,是把连接归还给连接池【连接池的Connection内部自己做好了】
public static void release(Connection conn, Statement st, ResultSet rs) {
 if (rs != null) {
 try {
 rs.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 rs = null;
 }
 if (st != null) {
 try {
 st.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 if (conn != null) {
 try {
 conn.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}

C3P0

C3P0数据源的性能更胜一筹,并且它可以使用XML配置文件配置信息!

步骤:

  1. 导入开发包【c3p0-0.9.2-pre1.jar】和【mchange-commons-0.2.jar】
  2. 导入XML配置文件【可以在程序中自己一个一个配,C3P0的doc中的Configuration有XML文件的事例】
  3. new出ComboPooledDataSource对象
private static ComboPooledDataSource comboPooledDataSource = null;
static {
 //如果我什么都不指定,就是使用XML默认的配置,这里我指定的是oracle的
 comboPooledDataSource = new ComboPooledDataSource("oracle");
}
public static Connection getConnection() throws SQLException {
 return comboPooledDataSource.getConnection();
}

Tomcat数据源

Tomcat服务器也给我们提供了连接池,内部其实就是DBCP

步骤:

  1. 在META-INF目录下配置context.xml文件【文件内容可以在tomcat默认页面的 JNDI Resources下Configure Tomcat's Resource Factory找到】
  2. 导入Mysql或oracle开发包到tomcat的lib目录下
  3. 初始化JNDI->获取JNDI容器->检索以XXX为名字在JNDI容器存放的连接池

context.xml文件的配置:

<Context>
 <Resource name="jdbc/EmployeeDB"
 auth="Container"
 type="javax.sql.DataSource"
 username="root"
 password="root"
 driverClassName="com.mysql.jdbc.Driver"
 url="jdbc:mysql://localhost:3306/zhongfucheng"
 maxActive="8"
 maxIdle="4"/>
</Context>
try {
 //初始化JNDI容器
 Context initCtx = new InitialContext();
 //获取到JNDI容器
 Context envCtx = (Context) initCtx.lookup("java:comp/env");
 //扫描以jdbc/EmployeeDB名字绑定在JNDI容器下的连接池
 DataSource ds = (DataSource)
 envCtx.lookup("jdbc/EmployeeDB");
 Connection conn = ds.getConnection();
 System.out.println(conn);
} 

使用dbutils框架

dbutils它是对JDBC的简单封装,极大简化jdbc编码的工作量

DbUtils类

提供了关闭连接,装载JDBC驱动,回滚提交事务等方法的工具类【比较少使用,因为我们学了连接池,就应该使用连接池连接数据库】

QueryRunner类

该类简化了SQL查询,配合ResultSetHandler使用,可以完成大部分的数据库操作,重载了许多的查询,更新,批处理方法。大大减少了代码量

ResultSetHandler接口

该接口规范了对ResultSet的操作,要对结果集进行什么操作,传入ResultSetHandler接口的实现类即可。

  • ArrayHandler:把结果集中的第一行数据转成对象数组。
  • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
  • BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
  • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
  • ColumnListHandler:将结果集中某一列的数据存放到List中。
  • KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
  • MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
  • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
  • ScalarHandler 将ResultSet的一个列到一个对象中。

使用DbUtils框架对数据库的CRUD

/*
* 使用DbUtils框架对数据库的CRUD
* 批处理
*
* */
public class Test {
 @org.junit.Test
 public void add() throws SQLException {
 //创建出QueryRunner对象
 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
 String sql = "INSERT INTO student (id,name) VALUES(?,?)";
 //我们发现query()方法有的需要传入Connection对象,有的不需要传入
 //区别:你传入Connection对象是需要你来销毁该Connection,你不传入,由程序帮你把Connection放回到连接池中
 queryRunner.update(sql, new Object[]{"100", "zhongfucheng"});
 }
 @org.junit.Test
 public void query()throws SQLException {
 //创建出QueryRunner对象
 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
 String sql = "SELECT * FROM student";
 List list = (List) queryRunner.query(sql, new BeanListHandler(Student.class));
 System.out.println(list.size());
 }
 @org.junit.Test
 public void delete() throws SQLException {
 //创建出QueryRunner对象
 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
 String sql = "DELETE FROM student WHERE id='100'";
 queryRunner.update(sql);
 }
 @org.junit.Test
 public void update() throws SQLException {
 //创建出QueryRunner对象
 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
 String sql = "UPDATE student SET name=? WHERE id=?";
 queryRunner.update(sql, new Object[]{"zhongfuchengaaa", 1});
 }
 @org.junit.Test
 public void batch() throws SQLException {
 //创建出QueryRunner对象
 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
 String sql = "INSERT INTO student (name,id) VALUES(?,?)";
 Object[][] objects = new Object[10][];
 for (int i = 0; i < 10; i++) {
 objects[i] = new Object[]{"aaa", i + 300};
 }
 queryRunner.batch(sql, objects);
 }
}

分页

分页技术是非常常见的,在搜索引擎下搜索页面,不可能把全部数据都显示在一个页面里边。所以我们用到了分页技术。

Oracle实现分页

/*
 Oracle分页语法:
 @lineSize---每页显示数据行数
 @currentPage----当前所在页
*/
SELECT *FROM (
 SELECT 列名,列名,ROWNUM rn
 FROM 表名
 WHERE ROWNUM<=(currentPage*lineSize)) temp
WHERE temp.rn>(currentPage-1)*lineSize;

Oracle分页原理简单解释

/*
 Oracle分页:
 Oracle的分页依赖于ROWNUM这个伪列,ROWNUM主要作用就是产生行号。
 分页原理:
 1:子查询查出前n行数据,ROWNUM产生前N行的行号
 2:使用子查询产生ROWNUM的行号,通过外部的筛选出想要的数据
 例子:
 我现在规定每页显示5行数据【lineSize=5】,我要查询第2页的数据【currentPage=2】
 注:【对照着语法来看】
 实现:
 1:子查询查出前10条数据【ROWNUM<=10】
 2:外部筛选出后面5条数据【ROWNUM>5】
 3:这样我们就取到了后面5条的数据
*/

Mysql实现分页

/*
 Mysql分页语法:
 @start---偏移量,不设置就是从0开始【也就是(currentPage-1)*lineSize】
 @length---长度,取多少行数据
*/
SELECT *
FROM 表名
LIMIT [START], length;
/*
 例子:
 我现在规定每页显示5行数据,我要查询第2页的数据
 分析:
 1:第2页的数据其实就是从第6条数据开始,取5条
 实现:
 1:start为5【偏移量从0开始】
 2:length为5
*/

总结:

  • Mysql从(currentPage-1)*lineSize开始取数据,取lineSize条数据
  • Oracle先获取currentPagelineSize条数据,从(currentPage-1)lineSize开始取数据



使用JDBC连接数据库实现分页

下面是常见的分页图片


配合图片,看下我们的需求是什么:

  1. 算出有多少页的数据,显示在页面上
  2. 根据页码,从数据库显示相对应的数据。


分析:

  1. 算出有多少页数据这是非常简单的【在数据库中查询有多少条记录,你每页显示多少条记录,就可以算出有多少页数据了】
  2. 使用Mysql或Oracle的分页语法即可


通过上面分析,我们会发现需要用到4个变量

  • currentPage--当前页【由用户决定的】
  • totalRecord--总数据数【查询表可知】
  • lineSize--每页显示数据的数量【由我们开发人员决定】
  • pageCount--页数【totalRecord和lineSize决定】
 //每页显示3条数据
 int lineSize = 3;
 //总记录数
 int totalRecord = getTotalRecord();
 //假设用户指定的是第2页
 int currentPage = 2;
 //一共有多少页
 int pageCount = getPageCount(totalRecord, lineSize);
 //使用什么数据库进行分页,记得要在JdbcUtils中改配置
 List<Person> list = getPageData2(currentPage, lineSize);
 for (Person person : list) {
 System.out.println(person);
 }
//使用JDBC连接Mysql数据库实现分页
public static List<Person> getPageData(int currentPage, int lineSize) throws SQLException {
 //从哪个位置开始取数据
 int start = (currentPage - 1) * lineSize;
 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
 String sql = "SELECT name,address FROM person LIMIT ?,?";
 List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{start, lineSize});
 return persons;
}
//使用JDBC连接Oracle数据库实现分页
public static List<Person> getPageData2(int currentPage, int lineSize) throws SQLException {
 //从哪个位置开始取数据
 int start = (currentPage - 1) * lineSize;
 //读取前N条数据
 int end = currentPage * lineSize;
 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
 String sql = "SELECT " +
 " name, " +
 " address " +
 "FROM ( " +
 " SELECT " +
 " name, " +
 " address , " +
 " ROWNUM rn " +
 " FROM person " +
 " WHERE ROWNUM <= ? " +
 ")temp WHERE temp.rn>?";
 List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{end, start});
 return persons;
}
public static int getPageCount(int totalRecord, int lineSize) {
 //简单算法
 //return (totalRecord - 1) / lineSize + 1;
 //此算法比较好理解,把数据代代进去就知道了。
 return totalRecord % lineSize == 0 ? (totalRecord / lineSize) : (totalRecord / lineSize) + 1;
}
public static int getTotalRecord() throws SQLException {
 //使用DbUtils框架查询数据库表中有多少条数据
 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
 String sql = "SELECT COUNT(*) FROM person";
 Object o = queryRunner.query(sql, new ScalarHandler());
 String ss = o.toString();
 int s = Integer.parseInt(ss);
 return s;
}

原文地址:https://dwz.cn/aldrP0zw

作者:Java3y

相关推荐

程序员:JDK的安装与配置(完整版)_jdk的安装方法

对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

学习笔记-Linux JDK - 安装&amp;配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

Linux新手入门系列:Linux下jdk安装配置

本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

测试员必备:Linux下安装JDK 1.8你必须知道的那些事

1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

Linux 下安装JDK17_linux 安装jdk1.8 yum

一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

在Ubuntu系统中安装JDK 17并配置环境变量教程

在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

如何在 Linux 上安装 Java_linux安装java的步骤

在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

linux安装jdk_linux安装jdk软连接

JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

linux安装jdk,全局配置,不同用户不同jdk

jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

运维大神教你在linux下安装jdk8_linux安装jdk1.7

1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

window和linux安装JDK1.8_linux 安装jdk1.8.tar

Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

取消回复欢迎 发表评论: