一.数据库操作原理
首先,先来回顾一下JDBC操作原理:
1.加载数据库驱动程序,数据库驱动程序通过classpath配置。
2.通过DriverManager类取得数据库连接对象。
3.通过Connection实例化PreparedStatement对象,编写SQL命令操作数据库。
4.数据库属于资源操作,操作完成后要关闭数据库以释放资源。
注:数据库连接时需要建立多次连接:在使用JDBC连接数据库时,程序会进行多个socket连接操作,所以这种传统的数据库操作性能是很低的。
由于每一个用户进行数据库操作时都会经过上面的三个步骤(1、2、4),但是每个用户对于数据库的操作却是不同的,例如,有的时更新数据库,有的是查询数据库,所以在进行数据库操作时,如果可以省略掉其中重复的三个步骤,只保留步骤3的话,那么性能肯定会有所提高,这实际上就是数据源产生的原因。
数据源操作的核心就是,在一个对象池中保存多个数据库连接(也称为数据库连接池,Connection Pool),这样以后再进行数据库操作时,直接从连接池中取出一条连接,当数据库操作完成之后,再将此连接放回到数据库连接池中,等待其他用户继续使用。这就好比现在的共享单车,一个地区放置的单车数量是有限的,当我们要使用,直接扫码开锁使用就行,当我们使用完则关锁付车费就行,而当车辆都在使用,我们要骑车只能等待别人使用完才行。
数据源中有以下几个元素:
(1)最小连接数:如果一个程序在使用时没有一个用户连接,则数据库最小应该维持的数据库连接数。
(2)最大连接数:在一个程序中一个数据库最多可以打开的数据库连接数。
(3)最大等待时间:当一个数据库连接池已经没有空闲数据库连接提供给用户使用时,其他用户等待的最大时间,如果在等待时间内有连接空闲出来,则可以继续使用;如果超过了最大等待时间,则用户无法取得数据库连接。
这样的程序可以使用java应用程序来实现,先在一个类集中保存多个数据库连接对象,之后通过控制类集达到连接池功能的实现,但是这种实现要考虑多线程的问题,而且以上三种问题也需要考虑,实现起来比较困难,而幸运的是在Tomcat4.1x版本之后已经支持了此操作,所以,在web开发中可以直接通过Tomcat即可实现数据库连接池的功能。
数据库连接池组件:如果不使用Tomcat实现数据库连接池,也可以从网上搜索各种数据库连接池的组件进行程序功能的实现,如Apache组织的C3P0组件。
二.在Tomcat中使用数据库连接池
在web容器中,数据库连接池都是通过数据源(javax.sql.DataSource)访问的,即可以通过javax.sql.DataSource类取得一个Connection对象,但是要想得到一个DataSource对象需要使用JNDI进行查找。
JNDI服务:
JNDI(Java Naming and Directory Interface,java命名及目录接口)是javaEE提供的一个服务,其服务的主要功能就是通过一个名称“key”查找到对应的一个对象“value”,这一设计也体现出了java程序的设计理念,通过key对应value,只要key不改变,则value可以随意修改。
从图中可以发现,客户端通过查询JNDI上绑定的名称取得一个DataSource对象,并且通过DataSource取得Connection Pool中保存的一个数据库连接。
注:此时的数据源连接池是在Tomcat上进行配置的,所以一定要将数据库的驱动程序复制到%TOMCAT_HOME%\common\lib文件夹中。
在server.xml文件中配置数据库连接池——%TOMCAT_HOME%\conf\server.xml
<Context docBase="D:\apache_tomcat\apache-tomcat-9.0.1\webapps\shop1" path="/" reloadable="true">
<Resource name="jdbc/mydb" ->配置一个数据库连接池资源,名称为jdbc/mydb
auth="Container" ->容器负责资源连接
type="javax.sql.DataSource" ->此数据源名称对应的类型为DataSource
maxActive="100" ->可以打开的最大连接数
maxIdle="30" ->维持的最小连接数
maxWait="10000" ->用户等待的最大时间
username="root" ->数据库用户名
password="111111" ->数据库密码
driverClassName="org.gjt.mm.mysql.Driver" ->数据库驱动程序
url="jdbc:mysql://localhost:3306/loginmanage" ->数据库名称
/>
</Context>
注:以上的注释只是为了说明各各项,具体使用时必须删除。另外,docBase的路径必须是一个web项目的路径。
以上的配置在<Context>节点中增加了一个<Resource>节点,用来表示配置的连接池选项,其中name属性指定的是此数据源的名称,此处为“jdbc/mydb”,这个名称也是以后程序中访问数据库资源时要查找的名称。
在<Resource>节点中的auth选项表示的是连接数据库的方法,可以有以下两种选择:
Container:容器将代表应用程序登录到资源管理器,一般使用此方式。
Application:应用程序必须程序化地登录到资源管理器。
提示:关于数据源名称地命名规范==》在开发中由于可以使用JNDI查询多种资源,为了清晰,在操作用于访问数据库的数据源中,可以使用jdbc/XXX的命名形式,这样可以直接从命名上知道这是一个操作数据库的命名资源。
上面配置的MySql数据库的驱动程序,如果要使用Oracle,则只需要替换相关的属性内容即可。
<Context docBase="D:\apache_tomcat\apache-tomcat-9.0.1\webapps\shop1" path="/" reloadable="true">
<Resource name="jdbc/mydb" ->配置一个数据库连接池资源,名称为jdbc/mydb
auth="Container" ->容器负责资源连接
type="javax.sql.DataSource" ->此数据源名称对应的类型为DataSource
maxActive="100" ->可以打开的最大连接数
maxIdle="30" ->维持的最小连接数
maxWait="10000" ->用户等待的最大时间
username="root" ->数据库用户名
password="111111" ->数据库密码
driverClassName="oracle.jdbc.driver.OracleDriver" ->数据库驱动程序
url="jdbc:oracle:thin:@localhost:1521:loginmanage" ->数据库名称
/>
</Context>
server.xml配置完成后就需要在一个web项目中配置web.xml文件,并在文件中指明要使用的数据源名称:
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/mydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
三.查找数据源
数据源使用JNDI方式进行查找,所以如果使用数据源取得数据库连接,则必须按照以下步骤进行:
1.初始化名称查找上下文:Context ctx=new InitialContext();
2.通过名称查找DataSource(数据源)对象:DataSource ds=(DataSource)cxt.lookup(JNDI名称);
3.通过DataSource取得一个数据库连接:Connection conn=ds.getConnection();
示例:通过数据源取得数据库连接——datasource.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="javax.naming.*,
javax.sql.*,
java.sql.*" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>通过数据源取得数据库连接</title>
</head>
<body>
<%
String DSNAME="java:comp/env/jdbc/mydb"; //JNDI名称
Context cxt=new InitialContext(); //初始化名称查找上下文
DataSource ds=(DataSource)cxt.lookup(DSNAME);//取得DataBase的实例
Connection conn=ds.getConnection(); //取得数据库连接
%>
<%=conn %>
<%
conn.close(); //将数据库连接直接放回池中去
%>
</body>
</html>
在本程序中可以发现,虽然真正在server.xml中配置的DataSource名称是“jdbc/mydb”,但是真正使用时却在前面加上了一个“java:comp/env/”前缀,这实际上是javaEE规定的一个环境命名上下文(Environment Naming Context (ENC)),主要是为了解决JNDI查找时的冲突问题。
注:java:comp/env/环境属性不一定到处都要使用==》对于一些高级服务器(如WebLogic、Websphere)由于本身已经设置好了此属性,所以在进行数据源查找时可以不用设置此属性,但是对于Tomcat必须设置,否则无法找到。
上面的程序直接打印出Connection对象,如果可以取得连接,则会输出一个对象信息;如果没有取得连接,则打印null。
在以后的开发中,就可以直接将数据源应用到项目中,例如,在DAO开发中经常使用到的DatabaseConnection类,就可以将其替换成数据源连接。
修改DAO中的DatabaseConnection.java:
package com.test;
import java.sql.Connection;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class DatabaseConnection {
//定义JNDI的查找名称
private static final String DSNAME="java:comp/env/jdbc/mydb";
private Connection conn=null;
public DatabaseConnection()throws Exception{//在构造方法中进行数据库连接
try {
Context cxt=new InitialContext();//初始化名称查找上下文
DataSource ds=(DataSource)cxt.lookup(DSNAME);//取得DataSource的实例
this.conn=ds.getConnection();//取得数据库连接
}catch(Exception e) {
e.printStackTrace();
}
}
public Connection getConnection() {//取得数据库连接
return this.conn;
}
public void close()throws Exception{//数据库关闭操作
if(this.conn!=null) { //避免空指向异常
try {
this.conn.close(); //数据库关闭
}catch(Exception e) { //抛出异常
throw e;
}
}
}
}
现在的程序只要数据源的名称不变,则数据库可以任意变换,这也充分体现了java的设计思想——可移植性。